js 小數點後16位精度計算錯誤問題

1、Number.isInteger() 方法用來判斷給定的參數是否爲整數。

由於 JavaScript 採用 IEEE 754 標準,數值存儲爲64位雙精度格式,數值精度最多可以達到 53 個二進制位(1 個隱藏位與 52 個有效位)。如果數值的精度超過這個限度,第54位及後面的位就會被丟棄,這種情況下,Number.isInteger可能會誤判。

Number.isInteger(3.0000000000000002) // true

上面代碼中,Number.isInteger的參數明明不是整數,但是會返回true。原因就是這個小數的精度達到了小數點後16個十進制位,轉成二進制位超過了53個二進制位,導致最後的那個2被丟棄了。

類似的情況還有,如果一個數值的絕對值小於Number.MIN_VALUE(5E-324),即小於 JavaScript 能夠分辨的最小值,會被自動轉爲 0。這時,Number.isInteger也會誤判。

Number.isInteger(5E-324) // false
Number.isInteger(5E-325) // true

上面代碼中,5E-325由於值太小,會被自動轉爲0,因此返回true

總之,如果對數據精度的要求較高,不建議使用Number.isInteger()判斷一個數值是否爲整數。

2、小數點後16位精度計算錯誤問題

ES6 在Number對象上面,新增一個極小的常量Number.EPSILON。根據規格,它表示 1 與大於 1 的最小浮點數之間的差。

對於 64 位浮點數來說,大於 1 的最小浮點數相當於二進制的1.00..001,小數點後面有連續 51 個零。這個值減去 1 之後,就等於 2 的 -52 次方。

Number.EPSILON === Math.pow(2, -52)
// true
Number.EPSILON
// 2.220446049250313e-16
Number.EPSILON.toFixed(20)
// "0.00000000000000022204"

Number.EPSILON實際上是 JavaScript 能夠表示的最小精度。誤差如果小於這個值,就可以認爲已經沒有意義了,即不存在誤差了。

引入一個這麼小的量的目的,在於爲浮點數計算,設置一個誤差範圍。我們知道浮點數計算是不精確的。

0.1 + 0.2
// 0.30000000000000004

0.1 + 0.2 - 0.3
// 5.551115123125783e-17

5.551115123125783e-17.toFixed(20)
// '0.00000000000000005551'

上面代碼解釋了,爲什麼比較0.1 + 0.20.3得到的結果是false

0.1 + 0.2 === 0.3 // false

Number.EPSILON可以用來設置“能夠接受的誤差範圍”。比如,誤差範圍設爲 2 的-50 次方(即Number.EPSILON * Math.pow(2, 2)),即如果兩個浮點數的差小於這個值,我們就認爲這兩個浮點數相等。

5.551115123125783e-17 < Number.EPSILON * Math.pow(2, 2)
// true

因此,Number.EPSILON的實質是一個可以接受的最小誤差範圍。

function withinErrorMargin (left, right) {
  return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2);
}

0.1 + 0.2 === 0.3 // false
withinErrorMargin(0.1 + 0.2, 0.3) // true

1.1 + 1.3 === 2.4 // false
withinErrorMargin(1.1 + 1.3, 2.4) // true

上面的代碼爲浮點數運算,部署了一個誤差檢查函數。

文檔

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章