浮點數在計算機硬件中表示爲以 2 爲基數(二進制)的小數。舉例而言,十進制的小數html
0.125
等於 1/10 + 2/100 + 5/1000 ,同理,二進制的小數python
0.001
等於0/2 + 0/4 + 1/8。這兩個小數具備相同的值,惟一真正的區別是第一個是以 10 爲基數的小數表示法,第二個則是 2 爲基數。git
不幸的是,大多數的十進制小數都不能精確地表示爲二進制小數。這致使在大多數狀況下,你輸入的十進制浮點數都只能近似地以二進制浮點數形式儲存在計算機中。架構
用十進制來理解這個問題顯得更加容易一些。考慮分數 1/3 。咱們能夠獲得它在十進制下的一個近似值函數
0.3
或者,更近似的,:工具
0.33
或者,更近似的,:spa
0.333
以此類推。結果是不管你寫下多少的數字,它都永遠不會等於 1/3 ,只是更加更加地接近 1/3 。code
一樣的道理,不管你使用多少位以 2 爲基數的數碼,十進制的 0.1 都沒法精確地表示爲一個以 2 爲基數的小數。 在以 2 爲基數的狀況下, 1/10 是一個無限循環小數orm
0.0001100110011001100110011001100110011001100110011...
在任何一個位置停下,你都只能獲得一個近似值。所以,在今天的大部分架構上,浮點數都只能近似地使用二進制小數表示,對應分數的分子使用每 8 字節的前 53 位表示,分母則表示爲 2 的冪次。在 1/10 這個例子中,相應的二進制分數是 3602879701896397 / 2 ** 55
,它很接近 1/10 ,但並非 1/10 。htm
大部分用戶都不會意識到這個差別的存在,由於 Python 只會打印計算機中存儲的二進制值的十進制近似值。在大部分計算機中,若是 Python 想把 0.1 的二進制對應的精確十進制打印出來,將會變成這樣
>>> 0.1 0.1000000000000000055511151231257827021181583404541015625
這比大多數人認爲有用的數字更多,所以Python經過顯示舍入值來保持可管理的位數
>>> 1 / 10 0.1
牢記,即便輸出的結果看起來好像就是 1/10 的精確值,實際儲存的值只是最接近 1/10 的計算機可表示的二進制分數。
有趣的是,有許多不一樣的十進制數共享相同的最接近的近似二進制小數。例如, 0.1
、 0.10000000000000001
、 0.1000000000000000055511151231257827021181583404541015625
全都近似於 3602879701896397 / 2 ** 55
。因爲全部這些十進制值都具備相同的近似值,所以能夠顯示其中任何一個,同時仍然保留不變的 eval(repr(x)) == x
。
在歷史上,Python 提示符和內置的 repr()
函數會選擇具備 17 位有效數字的來顯示,即 0.10000000000000001
。 從 Python 3.1 開始,Python(在大多數系統上)如今可以選擇這些表示中最短的並簡單地顯示 0.1
。
請注意這種狀況是二進制浮點數的本質特性:它不是 Python 的錯誤,也不是你代碼中的錯誤。 你會在全部支持你的硬件中的浮點運算的語言中發現一樣的狀況(雖然某些語言在默認狀態或全部輸出模塊下都不會 顯示 這種差別)。
想要更美觀的輸出,你可能會但願使用字符串格式化來產生限定長度的有效位數:
>>> format(math.pi, '.12g') # give 12 significant digits '3.14159265359' >>> format(math.pi, '.2f') # give 2 digits after the point '3.14' >>> repr(math.pi) '3.141592653589793'
必須重點了解的是,這在實際上只是一個假象:你只是將真正的機器碼值進行了舍入操做再 顯示 而已。
一個假象還可能致使另外一個假象。 例如,因爲這個 0.1 並不是真正的 1/10,將三個 0.1 的值相加也不必定能剛好獲得 0.3:
>>> .1 + .1 + .1 == .3
False
並且,因爲這個 0.1 沒法精確表示 1/10 的值而這個 0.3 也沒法精確表示 3/10 的值,使用 round()
函數進行預先舍入也是沒用的:
>>> round(.1, 1) + round(.1, 1) + round(.1, 1) == round(.3, 1)
False
雖然這些小數沒法精確表示其所要表明的實際值,round()
函數仍是能夠用來「過後舍入」,使得實際的結果值能夠作相互比較:
>>> round(.1 + .1 + .1, 10) == round(.3, 10)
True
二進制浮點運算會形成許多這樣的「意外」。 有關 "0.1" 的問題會在下面的「表示性錯誤」一節中更詳細地描述。 請參閱 浮點數的危險性 一文了解有關其餘常見意外現象的更詳細介紹。
正如那篇文章的結尾所言,「對此問題並沒有簡單的答案。」 可是也沒必要過於擔憂浮點數的問題! Python 浮點運算中的錯誤是從浮點運算硬件繼承而來,而在大多數機器上每次浮點運算獲得的 2**53 數碼位都會被做爲 1 個總體來處理。 這對大多數任務來講都已足夠,但你確實須要記住它並不是十進制算術,且每次浮點運算均可能會致使新的舍入錯誤。
雖然病態的狀況確實存在,但對於大多數正常的浮點運算使用來講,你只需簡單地將最終顯示的結果舍入爲你指望的十進制數值便可獲得你指望的結果。 str()
一般已足夠,對於更精度的控制可參看 Format String Syntax 中 str.format()
方法的格式描述符。
對於須要精確十進制表示的使用場景,請嘗試使用 decimal
模塊,該模塊實現了適合會計應用和高精度應用的十進制運算。
另外一種形式的精確運算由 fractions
模塊提供支持,該模塊實現了基於有理數的算術運算(所以能夠精確表示像 1/3 這樣的數值)。
若是你是浮點運算的重度用戶,你應該看一下數值運算 Python 包 NumPy 以及由 SciPy 項目所提供的許多其它數學和統計運算包。 參見 <https://scipy.org>。
Python 也提供了一些工具,能夠在你真的 想要 知道一個浮點數精確值的少數狀況下提供幫助。 例如 float.as_integer_ratio()
方法會將浮點數表示爲一個分數:
>>> x = 3.14159
>>> x.as_integer_ratio()
(3537115888337719, 1125899906842624)
因爲這是一個精確的比值,它能夠被用來無損地重建原始值:
>>> x == 3537115888337719 / 1125899906842624
True
float.hex()
方法會以十六進制(以 16 爲基數)來表示浮點數,一樣能給出保存在你的計算機中的精確值:
>>> x.hex() '0x1.921f9f01b866ep+1'
這種精確的十六進制表示法可被用來精確地重建浮點值:
>>> x == float.fromhex('0x1.921f9f01b866ep+1') True
因爲這種表示法是精確的,它適用於跨越不一樣版本(平臺無關)的 Python 移植數值,以及與支持相同格式的其餘語言(例如 Java 和 C99)交換數據.
另外一個有用的工具是 math.fsum()
函數,它有助於減小求和過程當中的精度損失。 它會在數值被添加到總計值的時候跟蹤「丟失的位」。 這能夠很好地保持總計值的精確度, 使得錯誤不會積累到能影響結果總數的程度:
>>> sum([0.1] * 10) == 1.0 False >>> math.fsum([0.1] * 10) == 1.0 True
本小節將詳細解釋 "0.1" 的例子,並說明你能夠怎樣親自對此類狀況進行精確分析。 假定前提是已基本熟悉二進制浮點表示法。
表示性錯誤 是指某些(實際上是大多數)十進制小數沒法以二進制(以 2 爲基數的計數制)精確表示這一事實形成的錯誤。 這就是爲何 Python(或者 Perl、C、C++、Java、Fortran 以及許多其餘語言)常常不會顯示你所期待的精確十進制數值的主要緣由。
爲何會這樣? 1/10 是沒法用二進制小數精確表示的。 目前(2000年11月)幾乎全部使用 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 <= 2**56 // 10 < 2**53
True
也就是說,56 是惟一的 N 值能令 J 剛好有 53 位。 這樣 J 的最佳可能值就是通過舍入的商:
>>> q, r = divmod(2**56, 10) >>> r 6
因爲餘數超過 10 的一半,最佳近似值可經過四捨五入得到:
>>> q+1 7205759403792794
這樣在 754 雙精度下 1/10 的最佳近似值爲:
7205759403792794 / 2 ** 56
分子和分母都除以二則結果小數爲:
3602879701896397 / 2 ** 55
請注意因爲咱們作了向上舍入,這個結果實際上略大於 1/10;若是咱們沒有向上舍入,則商將會略小於 1/10。 但不管如何它都不會是 精確的 1/10!
所以計算永遠不會「看到」1/10:它實際看到的就是上面所給出的小數,它所能達到的最佳 754 雙精度近似值:
>>> 0.1 * 2 ** 55 3602879701896397.0
若是咱們將該小數乘以 10**55,咱們能夠看到該值輸出爲 55 位的十進制數:
>>> 3602879701896397 * 10 ** 55 // 2 ** 55 1000000000000000055511151231257827021181583404541015625
這意味着存儲在計算機中的確切數值等於十進制數值 0.1000000000000000055511151231257827021181583404541015625。 許多語言(包括較舊版本的 Python)都不會顯示這個完整的十進制數值,而是將結果舍入爲 17 位有效數字:
>>> format(0.1, '.17f') '0.10000000000000001'
fractions
和 decimal
模塊可令進行此類計算更加容易:
>>> from decimal import Decimal >>> from fractions import Fraction >>> Fraction.from_float(0.1) Fraction(3602879701896397, 36028797018963968) >>> (0.1).as_integer_ratio() (3602879701896397, 36028797018963968) >>> Decimal.from_float(0.1) Decimal('0.1000000000000000055511151231257827021181583404541015625') >>> format(Decimal.from_float(0.1), '.17') '0.10000000000000001'