浮點數用來存儲計算機中的小數,與現實世界中的十進制小數不一樣的是,浮點數經過二進制的形式來表示一個小數。在深刻了解浮點數的實現以前,先來看幾個 Python 浮點數計算有意思的例子:python
0.1 == 0.10000000000000000000001
True
0.1+0.1+0.1 == 0.3
False
這些看起來違反常識的「錯誤」並不是 Python 的錯,而是由浮點數的規則所決定的,即便放到其它語言中結果也是這樣的。要理解計算機中浮點數的表示規則,先來看現實世界中十進制小數是如何表示的:code
1.234 = 1 + 1/10 + 2/100 + 3/1000
能夠用下面的公式來表示:圖片
$$d = \sum_{i=-n}^m10^i*d_i$$ip
其中 $d_i$ 是十進制中 0~9 的數字。而若是是一個二進制的小數:ci
1.001 = 1 + 0/2 + 0/4 + 1/8
能夠用下面的公式來表示:get
$$d = \sum_{i=-n}^m2^i*d_i$$it
其中 $d_i$ 是二進制中的 0 或 1。Python 中的浮點數都是雙精度的,也就說採用 64 位來表示一個小數,那這 64 位分別有多少用來表示整數部分和小數部分呢?根據 IEEE 標準,考慮到符號位,雙精度表示法是這樣分配的:io
$$d = s * \sum_{i=-52}^{11} 2^i*d_i$$class
也就是說用1位表示符號位,11位表示整數部分,52位表示小數部分。正如十進制中咱們沒法精確表示某些分數(如10/3
),浮點數中經過 d1/2 + d2/4 + ...
的方式也會出現這種狀況,好比上面的例子中,十進制中簡單的 0.1
就沒法在二進制中精確描述,而只能經過近似表示法表示出來:import
(0.1).as_integer_ratio()
(3602879701896397, 36028797018963968)
也就是說 0.1
是經過 3602879701896397/36028797018963968
來近似表示的,很明顯這樣近似的表示會致使許多差距很小的數字公用相同的近似表示數,例如:
(0.10000000000000001).as_integer_ratio()
(3602879701896397, 36028797018963968)
在 Python 中全部這些能夠用相同的近似數表示的數字統一採用最短有效數字來表示:
print(0.10000000000000001)
0.1
既然有些浮點數是經過近似值表示的,那麼在計算過程當中就很容易出現偏差,就像最開始的第二個例子同樣:
a = .1 + .1 + .1 b = .3 print(a.as_integer_ratio()) print(b.as_integer_ratio()) print(a == b)
(1351079888211149, 4503599627370496) (5404319552844595, 18014398509481984) False
爲了解決運算中的問題,IEEE 標準還指定了一個舍入規則(round),即 Python 中內置的 round
方法,咱們能夠經過舍入的方式取得兩個數的近似值,來判斷其近似值是否相等:
round(a, 10) == round(b, 10)
True
固然這種舍入的方式並不必定是可靠的,依賴於舍入的選擇的位數,位數太大,就失去了 round
的做用,過小,就會引入別的錯誤:
print(round(a, 17) == round(b, 17)) print(round(0.1, 1) == round(0.111, 1))
False True
Python 中使用更精確的浮點數能夠經過 decimal
和 fractions
兩個模塊,從名字上也能猜到,decimal
表示完整的小數,而 fractions
經過分數的形式表示小數:
from decimal import Decimal a = Decimal(0.1) b = Decimal(0.1000000000000001) c = Decimal(0.10000000000000001) print(a) print(b) print(c) a == b == c
0.1000000000000000055511151231257827021181583404541015625 0.10000000000000010269562977782697998918592929840087890625 0.1000000000000000055511151231257827021181583404541015625 False
from fractions import Fraction f1 = Fraction(1, 10) # 0.1 print(float(f1)) f3 = Fraction(3, 10) # 0.3 print(float(f3)) print(f1 + f1 + f1 == f3)
0.1 0.3 True
浮點數這些奇特的特性讓咱們不得不在使用的時候格外注意,尤爲是當有必定的精度要求的狀況下。若是真的是對精度要求較高且須要頻繁使用浮點數,建議使用更專業的 SciPy 科學計算包。