Python浮點數精度問題(包含解決方案)

2020-07-16 10:05:04
前面章節提到,Python 中浮點型別之間的運算,其結果並不像我們想象的那樣,例如:

>>> 0.1+0.2
0.30000000000000004
>>> 0.1+0.1-0.2
0.0
>>> 0.1+0.1+0.1-0.3
5.551115123125783e-17
>>> 0.1+0.1+0.1-0.2
0.10000000000000003

為什麼在計算這麼簡單的問題上,計算機會出現這樣的低階錯誤呢?真正的原因在於十進位制和數和二進位制數的轉換。

我們知道,計算機其實是不認識十進位制數,它只認識二進位制數,也就是說,當我們以十進位制數進行運算的時候,計算機需要將各個十進位制數轉換成二進位制數,然後進行二進位制間的計算。

以類似 0.1 這樣的浮點數為例,如果手動將其轉換成二進位制,其結果為:

0.1(10)=0.00011001100110011...(2)

可以看到,結果是無限迴圈的,也就是說,0.1 轉換成二進位制數後,無法精確到等於十進位制數的 0.1。同時,由於計算機儲存的位數是有限制的,所以如果要儲存的二進位制位數超過了計算機儲存位數的最大值,其後續位數會被捨棄(捨棄的原則是“0 捨 1 入”)。

這種問題不僅在 Python 中存在,在所有支援浮點數運算的程式語言中都會遇到,它不光是 Python 的 Bug。

明白了問題產生的原因之後,那麼該如何解決呢?就 Python 的浮點數運算而言,大多數計算機每次計算誤差不會超過 253,這對於大多數任務來說已經足夠了。

如果需要非常精確的結果,可以使用 decimal 模組(其實就是別人開發好的程式,我們可以直接拿來用),它實現的十進位制數運算適合會計方面的應用和有高精度要求的應用。例如:
#使用模組前,需要使用 import 引入
import decimal
a = decimal.Decimal("10.0")
b = decimal.Decimal("3")
print(10.0/3)
print(a/b)
執行結果為:

3.3333333333333335
3.333333333333333333333333333

可以看到,相比普通運算的結果,使用 decimal 模組得到的結果更精確。

如果 decimal 模組還是無法滿足需求,還可以使用 fractions 模組,例如:
#引入 decimal 模組
from fractions import Fraction
print(10/3)
print(Fraction(10,3))
執行結果為:

3.3333333333333335
10/3

可以看到,通過 fractions 模組能很好地解決浮點型別數之間運算的問題。

本節涉及到了有關模組的使用,讀者不用研究細節,會用即可,後續章節會詳細介紹模組。