先說結論
為什麼不等於?
因為浮點數表示小數的時候有精準度損失
為什麼會有精準度損失
因為計算機硬體儲存資料時,是以二進位制(10101010)形式進行
比如說每個位元組是 8 位,int 型別佔 4 個位元組,也就是 32 位精度;那麼 32 位的計算機精度可以存 2 的 32次方個資料。如下圖:
每位上面可以放兩個二進位制資料也就是 0 或者 1;一般最高位上是符號位(1表示負數,0表示正數),所以帶符號的型別資料應該是 31 個 2
2 2 2 ... 2(31個2),加上符號範圍就是 -2147483648 ~ 2147483647;當然也有無符號整形,暫不討論
那麼小數怎麼存呢?小數在計算機當中叫浮點型,JS 最終會由瀏覽器引擎轉成 C++,但是 JS 當中只有一種數值型別,那就是 number,那麼 number 在 C++ 是什麼型別呢;
我們暫且認為它是雙精度型別,也就是 double,C++ 中佔四個位元組,也就是 64 位儲存,整數儲存參考上面即可,重點說說浮點儲存
同樣 64 位可分為三部分,它的制定格式是以 IEEE 754 為標準:
第一部分:符號位(S),佔 1 位即第 63 位;
第二部分:指數域(E),佔 11 位即 52 位到 62 位,含 52 和 62;
第三部分:尾數域(F),佔 52 位即第 0 位到 51 位,含 51;
如果將一個小數轉換成二進位制 64 位怎麼表示,以 12.52571 為例
先轉換成二進位制(十進位制轉換成二進位制)(站長工具二進位制轉換)
- 12.52571 => 1100.100001101001010011101110001110010010111000011111
將其小數點向左偏移三位
- 1.100100001101001010011101110001110010010111000011111 * 2^3
得出結論
- 因為是整數,所以符號位 S 是 0;
因為向左偏移了三位,所以 E = 1023 + 3 = 1026(轉化為二進位制) => 10000000010,有 11 位,不夠前面補 0
- 為什麼要加1023?為什麼左移是加3,不是減3
- 尾數是(F)(小數點後面)100100001101001010011101110001110010010111000011111;
最終表示: 0 10000000010 100100001101001010011101110001110010010111000011111;
上面總長度是63位,差一位,最後面補零,即
0 10000000010 1001000011010010100111011100011100100101110000111110;
那麼12.52571的64位計算機儲存形式就是上面了;
回過頭看 0.1 + 0.2
上面的表達可能有些疑惑,肯定的,畢竟筆者也是參考的(權當筆記,供以後溫習),暫且不表;那麼 0.1 和 0.2 是怎麼轉的
這裡就有一個問題,0.1 和 0.2 轉成二進位制小數點後面是迴圈的
// 0.1 轉化為二進位制
0.0 0011 0011 0011 0011...(0011無限迴圈)
// 0.2 轉化為二進位制
0.0011 0011 0011 0011 0011...(0011無限迴圈)
由於尾數只有52位(52位之後的被計算機截掉了)
E = -4; F =1001100110011001100110011001100110011001100110011010 (52位)
E = -3; F =1.1001100110011001100110011001100110011001100110011010 (52位)
要讓兩個數相加,首先E需要相同,於是得出下面
E = -3; F =0.1100110011001100110011001100110011001100110011001101 (52位) //多餘位截掉
E = -3; F =1.1001100110011001100110011001100110011001100110011010 (52位)
上面兩個相加得出
E = -3; F = 10.0110011001100110011001100110011001100110011001100111
-------------------------------------------------------------------
E = -2; F = 1.00110011001100110011001100110011001100110011001100111
得出的結論就是
2^-2 * 1.00110011001100110011001100110011001100110011001100111
這個值轉換成真值,結果為: 0.30000000000000004
如何做到精準度
JavaScript 的型別 bigInt (ES8)中
TypeScript 也有這樣的型別
有解決精準度問題的 big.js、bigInt 庫
同樣有精準度缺失的語言
python
總結
因為 JavaScript 到最後會轉換為 C++ 去執行
在 IEEE754 標準中常見的浮點數數值表示有:單精準度(32位)和雙精準度(64位),JS 採用的是後者。浮點數與整數不同,一個浮點數既包含整數部分,又包含小數部分,因為其表示法的不同,需要分析為整數和小數部分,然後相加得到結果。0.1 和 0.2 先轉成二進位制,在轉換為同一維度計算,得到二進位制後,再轉換為十進位制後,就成了0.30000000000000004