組成原理|爲何計算機中0.3 + 0.6 等於 0.899999999...?

浮點數的不精確性

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

這是爲何?
咱們先看下面的介紹,最後文末會給出答案。算法

定點數的表示

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

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

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

浮點數的表示

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

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

1.符號爲表示正負0和1。
2.指數位,由於咱們也須要表示很小的數,指數位置須要負數,因此咱們在這裏用 1~254 映射到 -126~127 這 254個有正有負的數
3.有效數位,是一個 23 個比特組成的有效數位。咱們用f來表示。get

舉了栗子:
數學

浮點數相加的精度偏差

栗子1:讓一個值爲 2000 萬的 32 位浮點數和 1 相加,你會發現,+1 這個過程由於精度損失,被「徹底拋棄」了。it

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.三、0.6不能精確表示出來,例如0.5是2^(-1)是能夠被精確表示的。

相關文章
相關標籤/搜索