js數字位數太大導致參數精度丟失問題


 

 

最近遇到個比較奇怪的問題,js函數里傳參,傳一個位數比較大,打印arguments可以看到傳過來的參數已經改變。

 

然后查了一下,發現確實是js精度丟失造成的。我的解決方法是將數字型改成字符型傳輸,這樣就不會造成精度丟失了。如下圖:

 

 

JS 數字丟失精度的原因

計算機的二進制實現和位數限制有些數無法有限表示。就像一些無理數不能有限表示,如 圓周率 3.1415926...,1.3333... 等。JS 遵循 IEEE 754 規范,采用雙精度存儲(double precision),占用 64 bit。如圖

 

意義

  • 1位用來表示符號位
  • 11位用來表示指數
  • 52位表示尾數

 

浮點數,比如

1
2
0.1 >> 0.0001 1001 1001 1001…(1001無限循環)
0.2 >> 0.0011 0011 0011 0011…(0011無限循環)

此時只能模仿十進制進行四舍五入了,但是二進制只有 0 和 1 兩個,於是變為 0 舍 1 入。這即是計算機中部分浮點數運算時出現誤差,丟失精度的根本原因。

 

大整數的精度丟失和浮點數本質上是一樣的,尾數位最大是 52 位,因此 JS 中能精准表示的最大整數是 Math.pow(2, 53),十進制即 9007199254740992。

大於 9007199254740992 的可能會丟失精度

1
2
3
9007199254740992     >> 10000000000000...000  // 共計 53 個 0
9007199254740992 + 1 >> 10000000000000...001  // 中間 52 個 0
9007199254740992 + 2 >> 10000000000000...010  // 中間 51 個 0

 

實際上

1
2
3
4
9007199254740992 + 1  // 丟失
9007199254740992 + 2  // 未丟失
9007199254740992 + 3  // 丟失
9007199254740992 + 4  // 未丟失

 

結果如圖

 

以上,可以知道看似有窮的數字, 在計算機的二進制表示里卻是無窮的,由於存儲位數限制因此存在“舍去”,精度丟失就發生了。

想了解更深入的分析可以看這篇論文(又長又臭):What Every Computer Scientist Should Know About Floating-Point Arithmetic

 

三、解決方案

對於整數,前端出現問題的幾率可能比較低,畢竟很少有業務需要需要用到超大整數,只要運算結果不超過 Math.pow(2, 53) 就不會丟失精度。

對於小數,前端出現問題的幾率還是很多的,尤其在一些電商網站涉及到金額等數據。解決方式:把小數放到位整數(乘倍數),再縮小回原來倍數(除倍數)

1
2
// 0.1 + 0.2
(0.1*10 + 0.2*10) / 10 == 0.3  // true

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM