2011-05-13 50 views
17

我不知道這是否是一個明顯的錯誤,但在運行Python腳本來改變模擬參數時,我意識到delta = 0.29和delta = 0.58的結果丟失。關於調查,我注意到以下Python代碼:使用浮點數的Python四捨五入錯誤

for i_delta in range(0, 101, 1): 
    delta = float(i_delta)/100 

    (...) 

filename = 'foo' + str(int(delta * 100)) + '.dat' 

生成相同文件,δ= 0.28和0.29,與相同和0.57 0.58,其原因在於蟒返回浮動(29)/ 100作爲0.28999999999999998 。但這不是一個系統性的錯誤,並不在於它發生在每一個整數上。所以我創建了以下Python腳本:

import sys 

n = int(sys.argv[1]) 

for i in range(0, n + 1): 
    a = int(100 * (float(i)/100)) 
    if i != a: print i, a 

而且我看不到任何發生此舍入錯誤的數字模式。爲什麼這些特定數字會發生這種情況?

+4

這就是IEEE 754浮點數的工作原理。我建議你輪迴把浮動變回一個整數,而不是簡單地截斷。 – 2011-05-13 19:50:52

+1

這不是一個錯誤 - 它在很多不同的語言中都很常見。有一些步驟,但在這種情況下,最簡單的解決方案可能只是在文件名中使用idelta。請記住,默認情況下,idelta不會傳遞到循環外部。 – Tadeck 2011-05-13 19:56:52

+2

#StdSOAnswer_1。這就是浮點的工作原理。 – 2011-05-13 21:27:20

回答

27

任何無法用精確的冪數構建的數字都不能完全表示爲浮點數;它需要近似。有時最接近的近似值會小於實際值。

閱讀What Every Computer Scientist Should Know About Floating-Point Arithmetic

+0

在發佈相同鏈接之前,我確實沒有看到您鏈接到同一文檔。只是顯示了其很好的參考。 – 2011-05-13 20:00:29

+0

@ jimbob,我在原帖後一分鐘添加了鏈接。這是一個經典的,但我沒有馬上得到它。 – 2011-05-13 20:04:03

+5

對於Pythonistas,在[Python教程](http://docs.python.org/tutorial/floatingpoint.html)中還有一個更短(更容易閱讀)的章節來處理這個問題。 – 2011-05-13 20:13:24

16

由於floating point numbers的性質而衆所周知。

如果你想做十進制算術不浮點算術有libraries這樣做。

例如,

>>> from decimal import Decimal 
>>> Decimal(29)/Decimal(100) 
Decimal('0.29') 
>>> Decimal('0.29')*100 
Decimal('29') 
>>> int(Decimal('29')) 
29 

一般而言小數可能會過分和將仍然有在極少數情況下舍入誤差,當數目不具有有限的十進制表示(例如任何分數,其中分母不爲1或可被2或5整除 - 小數基數(10))。例如:

>>> s = Decimal(7) 
>>> Decimal(1)/s/s/s/s/s/s/s*s*s*s*s*s*s*s 
Decimal('0.9999999999999999999999999996') 
>>> int(Decimal('0.9999999999999999999999999996')) 
0 

所以它最好總是在將浮點數轉換爲整數之前將其捨棄,除非您想要floor函數。

>>> int(1.9999) 
1 
>>> int(round(1.999)) 
2 

另一種替代方法是使用分數類從fractions庫不近似。 (它只是在必要時加/減和乘以整數分子和分母)。

+0

嗯,實際上更好的例子是十進制(1)/十進制(3)*十進制(3),它不會以更高的精度產生1.0。 「當基數不是10」時應該是當分數不能準確表示爲基數10時。這個數字當然是10。 – 2013-04-20 14:38:17

+0

@DerekLitz - 同意,我的回答很sl。。你的例子更簡潔(雖然兩者同樣有效)。當該數字沒有10進制數的有限小數表示時,該數字應該寫入,當分母不能被2或5整除時,將發生在任何分數上。(當然,「分數不能完全用10進製表示。 「當然,它的基數爲10」也不完全正確,數字沒有基數,三分之一= 1 /(1 + 1 + 1)正好與基數無關。以基數10--1/3表示。) – 2013-04-20 16:14:08

+0

@dr_jimbob我喜歡上面的改進,但是,我不喜歡「數字沒有基數」的說法。也許差異是語義的,但詞的意義是重要的。一個數字應該表示一個值(或者如果您願意的話)。爲了創建一個編號系統,需要選擇一個基礎,需要選擇符號,並且我們可以更有效地進行溝通,然後進行簡單的計數,但我確定這就是您的意思:) – 2013-04-20 17:35:53