Python 浮点数运算
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
浮點(diǎn)數(shù)用來存儲計(jì)算機(jī)中的小數(shù),與現(xiàn)實(shí)世界中的十進(jìn)制小數(shù)不同的是,浮點(diǎn)數(shù)通過二進(jìn)制的形式來表示一個小數(shù)。在深入了解浮點(diǎn)數(shù)的實(shí)現(xiàn)之前,先來看幾個 Python 浮點(diǎn)數(shù)計(jì)算有意思的例子:
0.1 == 0.10000000000000000000001 True 0.1+0.1+0.1 == 0.3 FalseIEEE 浮點(diǎn)數(shù)表示法
這些看起來違反常識的“錯誤”并非 Python 的錯,而是由浮點(diǎn)數(shù)的規(guī)則所決定的,即使放到其它語言中結(jié)果也是這樣的。要理解計(jì)算機(jī)中浮點(diǎn)數(shù)的表示規(guī)則,先來看現(xiàn)實(shí)世界中十進(jìn)制小數(shù)是如何表示的:
1.234 = 1 + 1/10 + 2/100 + 3/1000可以用下面的公式來表示:
$$d = \sum_{i=-n}^m10^i*d_i$$
其中 $d_i$ 是十進(jìn)制中 0~9 的數(shù)字。而如果是一個二進(jìn)制的小數(shù):
1.001 = 1 + 0/2 + 0/4 + 1/8可以用下面的公式來表示:
$$d = \sum_{i=-n}^m2^i*d_i$$
其中 $d_i$ 是二進(jìn)制中的 0 或 1。Python 中的浮點(diǎn)數(shù)都是雙精度的,也就說采用 64 位來表示一個小數(shù),那這 64 位分別有多少用來表示整數(shù)部分和小數(shù)部分呢?根據(jù) IEEE 標(biāo)準(zhǔn),考慮到符號位,雙精度表示法是這樣分配的:
$$d = s * \sum_{i=-52}^{11} 2^i*d_i$$
也就是說用1位表示符號位,11位表示整數(shù)部分,52位表示小數(shù)部分。正如十進(jìn)制中我們無法精確表示某些分?jǐn)?shù)(如10/3),浮點(diǎn)數(shù)中通過 d1/2 + d2/4 + ... 的方式也會出現(xiàn)這種情況,比如上面的例子中,十進(jìn)制中簡單的 0.1 就無法在二進(jìn)制中精確描述,而只能通過近似表示法表示出來:
(0.1).as_integer_ratio() (3602879701896397, 36028797018963968)也就是說 0.1 是通過 3602879701896397/36028797018963968 來近似表示的,很明顯這樣近似的表示會導(dǎo)致許多差距很小的數(shù)字公用相同的近似表示數(shù),例如:
(0.10000000000000001).as_integer_ratio() (3602879701896397, 36028797018963968)在 Python 中所有這些可以用相同的近似數(shù)表示的數(shù)字統(tǒng)一采用最短有效數(shù)字來表示:
print(0.10000000000000001) 0.1浮點(diǎn)數(shù)運(yùn)算
既然有些浮點(diǎn)數(shù)是通過近似值表示的,那么在計(jì)算過程中就很容易出現(xiàn)誤差,就像最開始的第二個例子一樣:
a = .1 + .1 + .1 b = .3 print(a.as_integer_ratio()) print(b.as_integer_ratio()) print(a == b) (1351079888211149, 4503599627370496) (5404319552844595, 18014398509481984) False為了解決運(yùn)算中的問題,IEEE 標(biāo)準(zhǔn)還指定了一個舍入規(guī)則(round),即 Python 中內(nèi)置的 round 方法,我們可以通過舍入的方式取得兩個數(shù)的近似值,來判斷其近似值是否相等:
round(a, 10) == round(b, 10) True當(dāng)然這種舍入的方式并不一定是可靠的,依賴于舍入的選擇的位數(shù),位數(shù)太大,就失去了 round 的作用,太小,就會引入別的錯誤:
print(round(a, 17) == round(b, 17)) print(round(0.1, 1) == round(0.111, 1)) False TruePython 中使用更精確的浮點(diǎn)數(shù)可以通過 decimal 和 fractions 兩個模塊,從名字上也能猜到,decimal 表示完整的小數(shù),而 fractions 通過分?jǐn)?shù)的形式表示小數(shù):
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.1000000000000000055511151231257827021181583404541015625False 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總結(jié)
浮點(diǎn)數(shù)這些奇特的特性讓我們不得不在使用的時候格外注意,尤其是當(dāng)有一定的精度要求的情況下。如果真的是對精度要求較高且需要頻繁使用浮點(diǎn)數(shù),建議使用更專業(yè)的 SciPy 科學(xué)計(jì)算包。
轉(zhuǎn)載于:https://my.oschina.net/rainyear/blog/673802
總結(jié)
以上是生活随笔為你收集整理的Python 浮点数运算的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java 拖拽文件到文本框
- 下一篇: 基于 Docker 的现代软件供应链