打開瀏覽器控制檯 輸入 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)是能夠被精確表示的。