12.18 浮點運算為什麼不準?有人為0.30000000000000004建了個網站

浮點運算一直非常玄學。

任何一個學過小數點運算的擁有小學數學水平的人,都應該知道 1.0-0.9=0.1。然而當你把這個問題拋給可以計算出圓周率小數點後上百位、擁有超強算力的計算機的時候,結果總是非常迷。

你會發現,使用double或者float得出的計算結果,總是跟我們的預期有一點點差距,這就使得浮點運算容易讓人覺得“不精確”。

實際上,因為我們人類理解的數字是十進制,而計算機理解的數字是二進制。比如101.11這個數字,在人類眼中是一百零一點一一,而在電腦看來則是1 * 2^2 +0 *2^1 + 1*2^0 + 1*2^-1 + 1*2^-2 = 4+0+1+1/2+1/4 = 5.75,這個差距著實有點可怕的。

用二進制來表達十進制的小數點,彷彿在接兩根口徑不一致的管子,就會出現總是對不上的情況。比如為了表示0.2這個十進制下的數字,二進制只能用 1/(2^n)來表示小數,於是:


0.01 = 1/4 = 0.25 ,太大

0.001 =1/8 = 0.125 , 又太小

0.0011 = 1/8 + 1/16 = 0.1875 , 逼近0.2了

0.00111 = 1/8 + 1/16 + 1/32 = 0.21875 , 又大了

0.001101 = 1/8+ 1/16 + 1/64 = 0.203125 還是大

0.0011001 = 1/8 + 1/16 + 1/128 = 0.1953125 這結果不錯

0.00110011 = 1/8+1/16+1/128+1/256 = 0.19921875

……


當然,理論上是可以做到無限逼近的。然而我們的物理存儲介質的容量畢竟不是無限擴展的,那麼在編程語言中就會採用一些取巧的方式來進行權衡。比如java中規定double是32位,float是64位。但無論是多少位,總之是個博弈的結果,並不能完美的解決上述問題,甚至在不同平臺、硬件下,同樣的操作、同樣的位數,得出的結果都可能不一樣,更不用說在不同的編程語言環境下了。


比如0.1+0.2,結果如下:


浮點運算為什麼不準?有人為0.30000000000000004建了個網站

浮點運算為什麼不準?有人為0.30000000000000004建了個網站


有人因此建立了一個網站:

正是因為浮點運算的精度問題,使得其無法應用在像銀行這樣對數字極度敏感的場景中。銀行使用的是定點計算,簡單來說,就是把小數轉換為整數的一種計算。因為這樣操作,所有的賬目都是可以預測的。

但浮點運算可以表示比的範圍比整數更大、在大數運算中也更有優勢,它天生是為表示超大數或者無限值。因此在類似圖神經網絡這種注重數量大於精度的場景下,才是浮點運算大顯身手的地方。


分享到:


相關文章: