精確的浮點數運算 ?

cch
PyLadies Taiwan
Published in
5 min readJan 28, 2018

在上一次 Data Science — Numpy Basic ! 電子報最後的 reference 有提到 100 numpy exercises ,做到第15題時,遇到了一個浮點數精確度運算的問題:

* 為什麼 0.3 不等於 3 * 0.1 ? 😦

這個就要從機器底層看起,現今機器的 CPU 大多是通過 IEEE-754 標準來執行浮點數運算。簡單來說,IEEE 754 浮點數的組成為符號位 (sign bit)、指數 (exponent)、分數 (fraction) (如下圖)

當你進行浮點數運算時,必須先將數值轉為二進位 :

0.5 = 1/2 (1*2^-1)等於 0.1 (二進位)

0.75 = 1/2 + 1/4 (1*2^-1 + 1*2^-2)等於 0.11 (二進位)

0.875 = 1/2 + 1/4 + 1/8 (1*2^-1 + 1*2^-2 + 1*2^-3)等於 0.111 (二進位)

0.9375 = 1/2 + 1/4 + 1/8 + 1/16 (1*2^-1 + 1*2^-2 + 1*2^-3 + 1*2^-4)等於 0.1111 (二進位)

但如果是 0.1 :

0.1 = 1/16 + 1/32 + 1/256 + 1/512 + … (1*2^-4 + 1*2^-5 + 1*2^-8 + 1*2^-9+…)

無止盡下去的逼近 0.1,但是電腦的記憶體是有限的,因此只能存有限的二進位小數,沒辦法完全等於 0.1,所以 Python 會選擇一個近似值來取代

這裡要記得的是,Python 雖然 print 出來是 0.1,但其實存的是一個逼近 0.1 的二進位分數,電腦沒有辦法精確的存 0.1。

所以我們可以推知很多不同的浮點數 (十進位) 共用同一個二進位分數(0.1, 0.10000000000000001…)

以下兩個例子的等號,會是不相等的。

並且實際上是

那你會想說,那我換成用取四捨五入的方法 ( round( ) ),並且取小數點第一位,這樣應該就可以直接 0.1 + 0.1 + 0.1 = 0.3 了吧 !

很可惜的,還是失敗!

round ( 0.1 , 1 ) 等於 0.1,round( 0.3 , 1 ) 等於 0.3 ,那還是回到原點,0.1 + 0.1 + 0.1 == 0.3 這個算式

0.1 = 1/16 + 1/32 + 1/256 + 1/512 + …

浮點數在做計算時,0.1 不能精確的等於 1/10 ,0.3 不能精確的等於 3/10。

但你可以換個方式:

先進行浮點數的運算,再進行比較,這樣就可以成功的實現比較兩個浮點數。

* 是否真的要精確

一般來說,不建議直接將兩個浮點數進行大小比較,或是我們可以換個角度思考,不要拘泥於完全的相等,讓絕對誤差落在可以接受範圍內,這也是另一個方法。

但除了以下情況,我們非要精確的計算:

  1. 你無法接受一點點的誤差
  2. 你是在進行金融領域的相關計算

我們可以使用 Python Decimal module ( Decimal fixed point and floating point arithmetic ),他可以重現十進位的精準,並且指定小數點位數。

我們可以創建一個 Decimal 物件,傳入參數為 string 。

  1. 數值比較

這樣我們就可以精確的比較浮點數。

注意!這裡如果傳入的參數是 float type ,就無法精準的算出,因為這樣就跟前面所說的一樣,會受到底層存儲 float 的方式,而無法精準的計算。

2. 數值計算

前面說到 Decimal 可以去指定想要取到第幾位小數點,Decimal module default 為取到小數點第28位,我們可以用 getcontext( ) 來查看。

或是去指定欲想要的小數點位數。

getcontext( ) 是去取得 global 的 context ,利用 prec attribute 去指定 global 的小數點位數。如果想指定在某一區段使用特定的小數點位數,自行選擇想要的精準度,可以使用 with statement 以及 context manager

* 總結一下:

Python 使用的浮點數運算方式受到硬體存儲方式所影響,無法精準的算出答案,這時候你可以思考是否使用 Decimal module,但是使用前需要考慮到是否真的需要到如此精準,你的系統會因為小數點17位而受到嚴重的影響嗎?因為原生 Python 所提供浮點數運算相較起來,速度是比較快的,這會影響到在做大資料時運算的速度,所以完全取決於你的應用情形來決定。

Reference:

Decimal — Fixed and floating point math

Floating Point Arithmetic: Issues and Limitations

Python Cookbook, 3rd Edition (3.2 Performing Accurate Decimal Calculations)

浮點數乘法的誤差問題

談浮點數的比較

👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏

--

--