以前有同窗問過這樣一個問題: javascript
echo|awk '{print 3.99 -1.19 -2.80}' 4.44089e-16
相似的問題還有在 java 或者 javascript 中: html
23.53 + 5.88 + 17.64 = 47.05 23.53 + 17.64 + 5.88 = 47.050000000000004
爲何結果不是 0 或者不相等呢? java
若是你不能立馬回答出緣由,那說明你對浮點數計算的基本知識還不瞭解。 python
恰好最近 segmentfault.com 上也有同窗問了一樣的一個問題,如今整理下,以備忘。 mysql
浮點數是屬於有理數中某特定子集的數的數字表示,在計算機中用以近似表示任意某個實數。具體的說,這個實數由一個整數或定點數(即尾數)乘以某個基數(計算機中一般是2)的整數次冪獲得,這種表示方法相似於基數爲10的科學記數法。 程序員
浮點計算是指浮點數參與的運算,這種運算一般伴隨着由於沒法精確表示而進行的近似或舍入。 算法
爲了更好的理解,先來看一下10進制的純小數是怎麼表示的,假設有純小數D,它小數點後的每一位數字按順序造成一個數列:
{k1,k2,k3,...,kn}
那麼D又能夠這樣表示:
D = k1 / (10 ^ 1 ) + k2 / (10 ^ 2 ) + k3 / (10 ^ 3 ) + ... + kn / (10 ^ n )
推廣到二進制中,純小數的表示法即爲:
D = b1 / (2 ^ 1 ) + b2 / (2 ^ 2 ) + b3 / (2 ^ 3 ) + ... + bn / (2 ^ n )
如今問題就是怎樣求得b1,b2,b3,……,bn。算法描述起來比較複雜,仍是用數字來講話吧。聲明一下,1 / ( 2 ^ n )這個數比較特殊,我稱之爲位階值。 sql
例如0.456,第1位,0.456小於位階值0.5故爲0;第2位,0.456大於位階值0.25,該位爲1,並將0.456減去0.25得0.206進下一位;第3位,0.206大於位階值0.125,該位爲1,並將0.206減去0.125得0.081進下一位;第4位,0.081大於0.0625,爲1,並將0.081減去0.0625得0.0185進下一位;第5位0.0185小於0.03125……
最後把計算獲得的足夠多的1和0按位順序組合起來,就獲得了一個比較精確的用二進制表示的純小數了,同時精度問題也就由此產生,許多數都是沒法在有限的n內徹底精確的表示出來的,咱們只能利用更大的n值來更精確的表示這個數,這就是爲何在許多領域,程序員都更喜歡用double而不是float。 shell
對於開頭的問題,咱們再舉幾個例子(如下的例子採用 python 作示範): 數據庫
>>> 0.125 0.12500000000000000 >>> 0.1 0.10000000000000001 >>> 0.6 + 0.1 0.69999999999999996納尼?什麼會這樣?
>>> print("%.17lf" % (0.6 + 0.1)) 0.69999999999999996同理,浮點數之間用 >, <, == 來比較大小是不可取的。須要看兩個浮點數是否在合理的偏差範圍,若是偏差合理,即認爲相等。
x = 0.0 for i in range(100): x += 0.1 print("%.17lf" % x) #=> 9.99999999999998046 print(x) #=> 99.1,print 自動舍入,獲得了看似正確的結果在通常計算中,處理二進制浮點數須要用到不少技巧和技術。但在財務等運算中,必需要求徹底精確的結果,這時候,須要模擬 10 進制的浮點數。如 Python 中提供了 Decimal 模塊,容許使用者傳入浮點數的字符串進行模擬計算,避免精度問題。
from decimal import Decimal x = Decimal("0.0") # 注意:傳入字符串。若是傳入浮點數,那麼在計算以前精度就損失掉了 for i in range(100): x += Decimal("0.1") print("%.17lf" % x) #=> 10.00000000000000000 print(x) #=> 10.0關於 IEEE 浮點數,浮點數的大小比較等具體算法和細節,能夠觀看網易上麻省理工學院的這一集課程:
http://v.163.com/movie/2010/6/4/1/M6TCSIN1U_M6TCT0L41.html ,能夠從 05:39 處開始觀看。
這就是爲何交易系統的價格,金錢都不會使用float,double,包括數據庫的存儲。例如:mysql 能夠用 decimal ,若是你是用 java, 在商業計算中咱們要用 java.math.BigDecimal,注意:若是須要精確計算,非要用String來夠造BigDecimal不可!或者 sprintf 進行精度舍入。另外有些語言專門提供了處理金融數據的類型。
一、http://zh.wikipedia.org/zh-cn/IEEE_754
二、http://baike.baidu.com/view/339796.htm
三、http://www.ruanyifeng.com/blog/2010/06/ieee_floating-point_representation.html
四、http://segmentfault.com/q/1010000000267988
五、http://www.laruence.com/2013/03/26/2884.html PHP浮點數的一個常見問題的解答