python浮点数运算问题_python基础教程之. 浮点数运算:问题和局限
14. 浮點數運算:問題和局限?
浮點數在計算機硬件中表示為以 2 為底(二進制)的小數。例如,十進制小數
0.125
是1/10 + 2/100 + 5/1000 的值,同樣二進制小數
0.001
是 0/2 + 0/4 + 1/8 的值。這兩個小數具有相同的值,唯一真正的區別是,第一個小數是十進制表示法,第二個是二進制表示法。
不幸的是,大多數十進制小數不能完全用二進制小數表示。結果是,一般情況下,你輸入的十進制浮點數僅由實際存儲在計算機中的近似的二進制浮點數表示。
這個問題在十進制情況下很容易理解。考慮分數 1/3,你可以用十進制小數近似它:
0.3
或者更接近的
0.33
或者再接近一點的
0.333
等等。無論你愿意寫多少位數字,結果永遠不會是精確的 1/3,但將會越來越好地逼近 1/3。
同樣地,無論你使用多少位的二進制數,都無法確切地表示十進制值 0.1。1/10 用二進制表示是一個無限循環的小數。
0.0001100110011001100110011001100110011001100110011...
在任何有限數量的位停下來,你得到的都是近似值。
在一臺運行 Python 的典型機器上, Python 浮點數具有 53 位的精度,所以你輸入的十進制數0.1存儲在內部的是二進制小數
0.00011001100110011001100110011001100110011001100110011010
非常接近,但不完全等于 1/10。
由于解釋器顯示浮點數的方式,很容易忘記存儲在計算機中的值是原始的十進制小數的近似。Python 只打印機器中存儲的二進制值的十進制近似值。如果 Python 要打印 0.1 存儲的二進制的真正近似值,將會顯示
>>>0.1
0.1000000000000000055511151231257827021181583404541015625
這么多位的數字對大多數人是沒有用的,所以 Python 顯示一個舍入的值
>>>0.1
0.1
意識到在真正意義上這是一種錯覺是很重要的:機器中的值不是精確的 1/10,它顯示 的只是機器中真實值的舍入。一旦你用下面的數值進行算術運算,這個事實就變得很明顯了
>>>0.1 + 0.2
0.30000000000000004
注意,這是二進制浮點數的自然性質:它不是 Python 中的一個 bug ,也不是你的代碼中的 bug。你會看到所有支持硬件浮點數算法的語言都會有這個現象(盡管有些語言默認情況下或者在所有輸出模式下可能不會顯示 出差異)。
還有其它意想不到的。例如,如果你舍入2.675到兩位小數,你得到的是
>>>round(2.675, 2)
2.67
內置round()函數的文檔說它舍入到最接近的值,rounding ties away from zero。因為小數 2.675 正好是 2.67 和 2.68 的中間,你可能期望這里的結果是(二進制近似為) 2.68。但是不是的,因為當十進制字符串2.675轉換為一個二進制浮點數時,它仍然被替換為一個二進制的近似值,其確切的值是
2.67499999999999982236431605997495353221893310546875
因為這個近似值稍微接近 2.67 而不是 2.68,所以向下舍入。
如果你的情況需要考慮十進制的中位數是如何被舍入的,你應該考慮使用decimal模塊。順便說一下,decimal模塊還提供了很好的方式可以“看到”任何 Python 浮點數的精確值。
>>>from decimal import Decimal
>>>Decimal(2.675)
Decimal('2.67499999999999982236431605997495353221893310546875')
另一個結果是,因為 0.1 不是精確的 1/10,十個值為 0.1 數相加可能也不會正好是 1.0:
>>>sum = 0.0
>>>for i in range(10):
... sum += 0.1
...
>>>sum
0.9999999999999999
二進制浮點數計算有很多這樣意想不到的結果。“0.1”的問題在下面”誤差的表示”一節中有準確詳細的解釋。更完整的常見怪異現象請參見浮點數的危險。
最后我要說,“沒有簡單的答案”。也不要過分小心浮點數!Python 浮點數計算中的誤差源之于浮點數硬件,大多數機器上每次計算誤差不超過 2**53 分之一。對于大多數任務這已經足夠了,但是你要在心中記住這不是十進制算法,每個浮點數計算可能會帶來一個新的舍入錯誤。
雖然確實有問題存在,對于大多數平常的浮點數運算,你只要簡單地將最終顯示的結果舍入到你期望的十進制位數,你就會得到你期望的最終結果。關于如何精確控制浮點數的顯示請參閱格式化字符串的語法中str.format()方法的格式說明符。
14.1. 二進制表示的誤差?
這一節將詳細解釋“0.1”那個示例,并向你演示對于類似的情況自已如何做一個精確的分析。假設你已經基本了解浮點數的二進制表示。
二進制表示的誤差 指的是這一事實,一些(實際上是大多數) 十進制小數不能精確地用二進制小數表示。這是為什么 Python(或者 Perl、 C、 C++、 Java、 Fortran和其他許多語言)通常不會顯示你期望的精確的十進制數的主要原因:
>>>0.1 + 0.2
0.30000000000000004
這是為什么?1/10 和 2/10 不能用二進制小數精確表示。今天(2010 年 7 月)幾乎所有的機器都使用 IEEE-754 浮點數算法,幾乎所有的平臺都將 Python 的浮點數映射成 IEEE-754“雙精度浮點數”。754 雙精度浮點數包含 53 位的精度,所以輸入時計算機努力將 0.1 轉換為最接近的 J/2**N 形式的小數,其中J 是一個 53 位的整數。改寫
1 / 10 ~= J / (2**N)
為
J ~= 2**N / 10
回想一下?J?有 53 位(>= 2**52但<2**53),所以 N 的最佳值是 56:
>>>2**52
4503599627370496
>>>2**53
9007199254740992
>>>2**56/10
7205759403792793
即 56 是 N ?保證 J 具有 53 位精度的唯一可能的值。J 可能的最佳值是商的舍入:
>>>q, r = divmod(2**56, 10)
>>>r
6
由于余數大于 10 的一半,最佳的近似值是向上舍入:
>>>q+1
7205759403792794
因此在 754 雙精度下 1/10 的最佳近似是 J 取大于 2**56 的那個數,即
7205759403792794 / 72057594037927936
請注意由于我們向上舍入,這其實有點大于 1/10;如果我們沒有向上舍入,商數就會有點小于 1/10。但在任何情況下它都不可能是精確的 1/10!
所以計算機從來沒有”看到”1/10: 它看到的是上面給出的精確的小數,754 雙精度下可以獲得的最佳的近似了:
>>>.1 * 2**56
7205759403792794.0
如果我們把這小數乘以 10**30,我們可以看到其(截斷后的)值的最大?30 位的十進制數:
>>>7205759403792794 * 10**30 // 2**56
100000000000000005551115123125L
也就是說存儲在計算機中的精確數字約等于十進制值 0.100000000000000005551115123125。以前 Python 2.7 和 Python 3.1 版本中,Python 四舍五入到 17 個有效位,給出的值是 ‘0.10000000000000001’。在當前版本中,Python 顯示一個最短的十進制小數,它會正確舍入真實的二進制值,結果就是簡單的‘0.1’。
總結
以上是生活随笔為你收集整理的python浮点数运算问题_python基础教程之. 浮点数运算:问题和局限的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 图像条纹检测 python_【连载4.5
- 下一篇: shell 取中间行的第一列_shell