js小數精度問題


一、場景

0.1 + 0.2 = 0.30000000000000004 
1.5 - 1.2 = 0.30000000000000004
19.9 * 100 = 1989.9999999999998
0.3 / 0.1 = 2.9999999999999996
 
二、原因
js中的數字只有 Number這種類型,其存采用的64位雙精度浮點數(1位符號位、11位指數位,52位小數位),如下:
做運算操作時會將10進制小數轉換為2進制小數,整數部分采用除2取余法,如下:
   
 
小數部分采用的“乘2取整,順序排位法”,以0.1為例,如下:
   
問題是不可能乘以2后恰好為整數,此時就會導致無限循環,而存儲結構中的尾數部分最多只能表示 52 位,超出的會被舍棄掉,所以機器中存儲的就是一個近似值,這就是導致小數精度的問題所在
 
三、解決辦法
 
思路1:縮放法
 
整數不會有精度問題,因此先將小數放大為整數進行運算,運算完再將結果縮為小數。
 
1.先找出小數位數最多的數字作為x(例:a為0.1,b為0.22,那么x就是2),然后每個數字都乘以10的x次方變為為整數,方法如下
 
 Number(`${n}e${x}`)  //n為需要變換的數字,e在計算機中表示底數為10,x為小數位數。 轉換也可以寫成n*Math.pow(10,x)

計算完成后,加減法用結果除以10的x次方,將結果變為小數。

result / Math.pow(10,x)

乘法:

result/Math.pow(10,x*2) //如果是3個數,則將2換為3,以此類推

除法

假設a和b同時都放大了10的x次方,那么商相當於沒有放大(也就是放大了10^0 = 1),由於商可能為小數,因此可以利用除以一個數,等於乘以該數的導數這個公式,調用乘法再次進行轉換。

/**
 * 計算小數位數
*/
function digitLength(num) {
   const arr = num.toString().split('.');
   return arr[1]!==undefined ? arr[1].length : 0;
}
/**
 * 乘法
 */
 function times(a,b) {
    const x = digitLength(a),y = digitLength(b);
    const max = Math.max(x,y);
    const int_a = Number(`${a}e${max}`);
    const int_b = Number(`${b}e${max}`);
    const result = int_a * int_b;
    return result / Math.pow(10,x*2);
}
/**
 * 除法
 */
 function divide(a,b) {
    const x = digitLength(a),y = digitLength(b);
    const max = Math.max(x,y);
    const int_a = Number(`${a}e${max}`);
    const int_b = Number(`${b}e${max}`);
    const result = int_a / int_b;
    return times(result, 1); //result可能為小數,因此使用乘法,1的倒數還是1
}

  

思路2:

使用toFixed()降低小數精度

 

其他:

使用mathjs函數庫


免責聲明!

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



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