JavaScript浮點數比較問題
阿新 • • 發佈:2019-01-14
整數和浮點數
JavaScript 內部,所有數字都是以64位浮點數形式儲存,即使整數也是如此。所以,1
與1.0
是相同的,是同一個數。
1 === 1.0 // true
這就是說,JavaScript 語言的底層根本沒有整數,所有數字都是小數(64位浮點數)。容易造成混淆的是,某些運算只有整數才能完成,此時 JavaScript 會自動把64位浮點數,轉成32位整數,然後再進行運算,參見《運算子》一章的”位運算“部分。
由於浮點數不是精確的值,所以涉及小數的比較和運算要特別小心。
0.1 + 0.2 === 0.3 // false 0.3 / 0.1 // 2.9999999999999996 (0.3 - 0.2) === (0.2 - 0.1) // false
數值精度
根據國際標準 IEEE 754,JavaScript 浮點數的64個二進位制位,從最左邊開始,是這樣組成的。
- 第1位:符號位,
0
表示正數,1
表示負數 - 第2位到第12位(共11位):指數部分
- 第13位到第64位(共52位):小數部分(即有效數字)
符號位決定了一個數的正負,指數部分決定了數值的大小,小數部分決定了數值的精度。
指數部分一共有11個二進位制位,因此大小範圍就是0到2047。IEEE 754 規定,如果指數部分的值在0到2047之間(不含兩個端點),那麼有效數字的第一位預設總是1,不儲存在64位浮點數之中。也就是說,有效數字這時總是1.xx...xx
xx..xx
的部分儲存在64位浮點數之中,最長可能為52位。因此,JavaScript 提供的有效數字最長為53個二進位制位。
(-1)^符號位 * 1.xx...xx * 2^指數部分
上面公式是正常情況下(指數部分在0到2047之間),一個數在 JavaScript 內部實際的表示形式。
精度最多隻能到53個二進位制位,這意味著,絕對值小於2的53次方的整數,即-253到253,都可以精確表示。
Math.pow(2, 53) // 9007199254740992 Math.pow(2, 53) + 1 // 9007199254740992 Math.pow(2, 53) + 2 // 9007199254740994 Math.pow(2, 53) + 3 // 9007199254740996 Math.pow(2, 53) + 4 // 9007199254740996
上面程式碼中,大於2的53次方以後,整數運算的結果開始出現錯誤。所以,大於2的53次方的數值,都無法保持精度。由於2的53次方是一個16位的十進位制數值,所以簡單的法則就是,JavaScript 對15位的十進位制數都可以精確處理。
Math.pow(2, 53)
// 9007199254740992
// 多出的三個有效數字,將無法儲存
9007199254740992111
// 9007199254740992000
上面示例表明,大於2的53次方以後,多出來的有效數字(最後三位的111
)都會無法儲存,變成0。