JavaScript:精度丟失與大整數運算

原生的 JavaScript 無法進行大整數的運算
JavaScript Number 採用 IEEE 754 倍精度 64 位浮點數來儲存
數字的格式為: (±) b * 2 ^ e

正負號:用 1 bit 存
底數 b:用 52bits 來存
指數 e:用 11bits 來存,ECMAScript 規範 e 的範圍為 [-1074, 971]

最大整數

所以最大整數就是
(2 ^ 53 – 1) * 2 ^ 971 = 1.7976931348623157e+308
這個值就是 Number.MAX_VALUE 值
再大一點的數字
在底數多 1 以內仍然是 1.7976931348623157e+308
也就是說 Number.MAX_VALUE 加上超過 0.0000000000000001e+308 就會變成 Infinity
最小整數是有負號的:- 1.7976931348623157e+308

最接近 0 的整數

最接近 0 的整數則是
1 * (-1074) = 5e-324
這個值就是 Number.MIN_VALUE 值
再小一點的數字
在底數多 1 以內仍然是仍然是 5e-324
再更小的話一律都會變成 0

精度丟失問題
0.1 + 0.2 == 0.3 // false
0.1 + 0.2 == 0.30000000000000004 // true
9999999999999999 == 10000000000000000 // true

0.1, 0.2, 10000000000000000 都是出現 52bits 無法保存的底數 (循環小數或是位數過多等等)

解決辦法

因為 Number 無法存更精准的數字,所以現在普遍的解決辦法是用字串來存,再設計 function 來把逐位字串 parseInt 後做加減乘除跟模數等等運算。

例如 add 的 function 會使用迴圈對每一位數做相加再判斷是不是要在下一位進位,multi 的 function 會使用迴圈做相乘再判斷 Math.floor(temp/10) 看要進位多少。
諸如這樣的方法就可以借由字串保留更精准的數字

目前看到兩個看起來比較多人用的 libaray,或許比較可靠一點:

math.js

– 包括各種數學運算的,github 上有 600 多 stars
https://github.com/josdejong/mathjs

BigInt.js

– 只大整數運算的,從 2000 年維護至今
http://leemon.com/crypto/BigInt.js

應用

以往前端會用到浮點數或是大整數的機會很少,所以 JavaScript 也很少需要這整運算,不過隨著 node.js 的流行,Server 端加密(SHA等等)、各種演算法與計算量大量增加,往後可能會越來越常用到。

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 變更 )

Twitter picture

You are commenting using your Twitter account. Log Out / 變更 )

Facebook照片

You are commenting using your Facebook account. Log Out / 變更 )

Google+ photo

You are commenting using your Google+ account. Log Out / 變更 )

連結到 %s