1. 程式人生 > >JavaScript浮點數比較問題

JavaScript浮點數比較問題

整數和浮點數

JavaScript 內部,所有數字都是以64位浮點數形式儲存,即使整數也是如此。所以,11.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。