【Python自學】python 浮點數的誤差以及其解決方式

 在Python環境當中,輸入以下的代碼:

(一)#浮點數的誤差

x = float(0.1+ 0.2)

print(x)

 

Run的結果:0.30000000000000004

什麼?難道跟我學的不一樣嗎?

 

再看看另外一個例子:

(二)#浮點數的誤差

x = float(1-9/10)

print(x)

Run的結果:0.09999999999999998

 

蛤?根據我粗淺的數學計算能力,1-9/10=1/10,或者說0.1,再怎樣也不會是0.09999999999999998這種詭異的數字。

 

那不然......我們不計算,看一下直接print(1/10)是什麼答案?

(三)#浮點數的誤差

x = float(1/10)

print(x)

Run的結果:0.1

 

???根據(二)(三),所以1-9/10不等於1/10嗎?

這就是所謂的「浮點數誤差」!

 

進入正題!

為什麼 float 計算上有時候會不準?

原因:float 使用 IEEE 754 二進位浮點數標準來表示數字。

這也不是只有Python會這樣,所有使用 IEEE 754 的語言都這樣(CJavaJavaScript 都一樣)。

某些十進位數字無法用二進位「剛好表示」。因此像 0.1 + 0.2 在內部儲存時會有微小誤差。

 

其中道理是這樣的,先來討論「十進位與二進位」

1/3 在十進位中是:0.333333333333...(無限循環)

明明是很簡單的分數,卻表示不完,我們稱之為循環小數。

0.1 在二進位中也是「無限循環小數」

只是電腦只能存有限位數,只好在可以儲存的極限數後面截斷,誤差就在這裡產生。

舉個例子來說:0.1 換成二進位會變怎樣?

0.0001100110011001100110011001100110011001100110011...(0011無限循環)

這串 0011 會無限重複,就像 0.33333 無限重複一樣。

但電腦只能存 53 位有效位元(IEEE 754 double precision),所以電腦會把它 截成 53 位,最後就會長成下面這樣

0.00011001100110011001100110011001100110011001100110011 (只保留前面一段)

然後python會截斷後再轉回十進位

0.1000000000000000055511151231257827021181583404541015625...

 

這就是浮點數的誤差來源

 

 

那怎麼辦?

最常見的解決方法(共 3 種)

方法一:用 round() 做四捨五入【最常見也最暴力的解法】

result = round(0.1 + 0.2, 2)

print(result)  

 

Run的結果:0.3

 

誤差在小數點這麼後面的位置,其實並不影響平常常用的數學運算,只要把後面誤差的部份給四捨五入處理掉,就並無大礙了。

這種用法適合顯示一般結果、日常代碼;但是想也知道,並不適合需要精確計算的情況,像金額帳務、天文計算……等。

 

 

方法二:用 Decimal處理

Decimal(處理金額/高精度運算最佳)

from decimal import Decimal

# Decimal的用法,注意參數是字串型別

a = Decimal("0.1")

b = Decimal("0.2")

print(a + b)  

 

Run的結果:0.3

 

那為什麼Decimal可以,float不行?

因為Decimal使用 十進位(base 10)儲存與計算,不是二進位浮點數。當它收到字串時,可以「逐位解析」文字數值精確十進位表示。

也就是說,只要字串裡的數字是我們一般所想的十進位小數,Decimal 就能無誤差地表示。

 

重點:一定要用字串來建立 Decimal,用float去建立的話就白忙一場了

 

方法三:用 Fraction(分數計算)

from fractions import Fraction

a = Fraction(1, 10)   # 代表 1/10

b = Fraction(2, 10)   # 代表 2/10

print(a + b)        

 

Run的結果:3/10

 

直接用分數計算,不要進到小數的世界裡,就不會有十進位轉二進位所導致的誤差情況。

適合需要保留「精確分數結果」的數學題。這種情況讓我想起一段學習數學的往事,國小的時候,數學應用題的計算結果,往往會用小數表示;然而上了國中之後,數學老師就嚴禁我們用小數表示計算結果,一定要用「分數」表示。

我不確定跟這一篇想表達的「浮點數誤差」有沒有關係,我只是想分享這件事。

 

 

 

留言

這個網誌中的熱門文章

常見的化痰粉愛克痰(小鳥粉)怎麼吃?|化痰粉成人及小孩的使用劑量|紅色與藍色比較

麻將教學懶人包|從規則到牌理的完整觀念整理(附實戰心得)

麻將新手必看!不知道聽什麼牌怎麼辦?超多種實戰聽牌範例,教你怎麼判斷胡牌機會