1. 程式人生 > >JavaScript 數字精度問題

JavaScript 數字精度問題

雙精度浮點數表示法

JavaScript 數字採用的是 IEEE-754 標準 的雙精度浮點數表示法。雙精度儲存佔用 64bit,其中尾數佔 52bit,這決定了它能表示的最大安全整數為 253-1。所以說,JavaScript 能夠準確表示的整數範圍在 -253 到 253 之間(不含兩個端點),超過這個範圍就無法精確表示,比如:

2**53               // 9007199254740992
9007199254740992    // 9007199254740992
9007199254740993    // 9007199254740992

事實上,出現這個問題的機率極小,畢竟很少有業務需要用到超大整數。相對而言,另一個問題比它嚴重的多。另一個問題是,二進位制浮點數表示法不能精確的表示十進位制小數,比如十進位制小數 0.1 轉為二進位制就成了 0.0001100110011…(無限迴圈)。因此,JavaScript 在進行小數計算的時候會出現一些問題,比如:

.2 - .1 // 0.1
.3 - .2 // 0.09999999999999998
.4 - .3 // 0.10000000000000003

儘管計算結果非常接近最終值,但在某些特定場景下,比如進行重要的金融計算,這個問題就不容小覷了。

Number.prototype.toFixed() 方法的問題

toFixed() 方法使用定點數表示法來格式化一個數字,返回值為 String 型別。值得注意的是該方法的舍入法則並不是四捨五入,很多人對此有誤解。而且,不同瀏覽器對 toFixed() 方法的處理結果是不同的。比如:(2.445).toFixed(2) 在 IE 中的執行結果為 "2.45",但是在 Chrome 中的執行結果為 "2.44"

。曾經某商城出現不同瀏覽器上顯示價格不一樣的問題就是使用了 toFixed() 方法導致的。總的來說,toFixed() 方法不能用來處理這種小數舍入問題。

Math.round() 方法的問題

Math.round() 函式返回一個數字四捨五入後最接近的整數。不同於 toFixed() 方法,Math.round() 方法本身沒有問題,你可以放心的使用它對一個數字進行四捨五入。但是實際場景中,我們往往需要處理的是計算結果而非字面量。問題就出在這個計算上,比如 2.445 * 100 的計算結果不是 244.5 而是 244.49999999999997,這個時候再使用 Math.round() 方法,其結果自然和預期的不一樣了。

Math.round(244.5)       // 245
Math.round(2.445 * 100) // 244
2.445 * 100 === 244.5   // false

使用大整數進行計算來解決問題

或許 JavaScript 未來會支援十進位制數字型別以避免這些問題。在這之前,我們需要使用大整數進行計算來解決問題。比如,要使用整數“分”而不是小數“元”進行基於貨幣單位的運算。下面例子演示將 2445 釐表示成 x.xx 元(四捨五入)。

(Math.round(2445 / 10) / 100).toFixed(2)    // "2.45"