組成原理|為什么計算機中0.3 + 0.6 等於 0.899999999...?


浮點數的不精確性

打開瀏覽器控制台 輸入 0.3 + 0.6 ,結果輸出了0.8999999,不相信你可以按F12打開控制台試一試O(∩_∩)O

這是為什么?
我們先看下面的介紹,最后文末會給出答案。

定點數的表示

使用BCD編碼:用二進制來表示十進制的編碼方式

編碼過程:我們用 4 個比特來表示 0~9 的整數,那么 32 個比特就可以表示8個這樣的數
然后我們把最右邊的 2 個 0~9 的整數,當成小數部分;
把左邊 6 個 0~9 的整數,當成整數部分。
這樣,我們就可以用 32 個比特,來表示從 0 到 999999.99 這樣 1 億個實數了。

適用途徑:銀行、商家精確到“分0.01 ”的交易。
缺點:浪費比特啊,而且這樣的表示方式沒辦法同時表示很大的數字和很小的數字。

浮點數的表示

IEEE的標准,它定義了兩個基本的格式:float單精度 和 double雙精度

下面是單精度的表示方法:

1.符號為表示正負0和1。
2.指數位,因為我們也需要表示很小的數,指數位置需要負數,所以我們在這里用 1~254 映射到 -126~127 這 254個有正有負的數
3.有效數位,是一個 23 個比特組成的有效數位。我們用f來表示。

舉了栗子:

浮點數相加的精度誤差

栗子1:讓一個值為 2000 萬的 32 位浮點數和 1 相加,你會發現,+1 這個過程因為精度損失,被“完全拋棄”了。

float a = 20000000.0f;
float b = 1.0f;
float sum = a + b;
print sum //發現sum的值還是2000萬,而不是 200000001

為什么出現上述情況,因為兩個浮點數相加的運算是需要移位的,
過大或者過小的數移位之后可能會出現有效位數消失的情況,此部分內容可以搜尋相關資料:浮點數相加運算過程、浮點數的二進制表示

栗子2:將一個等於1.0的浮點數累加循環加2000萬次,發現結果是1600萬左右,你可以用java實現下面代碼,看看結果~~

為什么?也是浮點數加法產生的精度誤差


    float sum = 0.0f;
    for (int i = 0; i < 20000000; i++) {
    	float x = 1.0f;
    	sum += x;    	
    }
    print sum // sum 約等於1.6777216E7

解決方案:Kahan Summation 算法


    float sum = 0.0f;
    float c = 0.0f;
    for (int i = 0; i < 20000000; i++) {
    	float x = 1.0f;
    	float y = x - c;
    	float t = sum + y;
    	c = (t-sum)-y;
    	sum = t;    	
    }
   print sum;//2000萬

在每次的計算過程中,都用一次減法,把當前加法計算中損失的精度記錄下來然后在后面的循環中,把這個精度損失放在要加的小數上,再做一次運算。
該算法的數學證明參見:Wikipedia 鏈接

所以回到文章開頭的問題,0.3 + 0.6 為什么不等於0.9,
就是因為計算機用浮點數表示法來表示浮點數,只能精確表示 2^x (2的x次方)這種數0.3、0.6不能精確表示出來,例如0.5是2^(-1)是可以被精確表示的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM