js浮點數精度丟失問題及如何解決js中浮點數計算不精準

js中進行數字計算時候,會出現精度偏差的問題。先來看一個實例:html

 

console.log(0.1+0.2===0.3);//false編程

console.log(0.1+0.1===0.2);//true編程語言

上面第一個的輸出會超出咱們的常識,正常應該爲true,這裏爲何會是false呢,直接運行會發現0.1+0.2在js中計算的結果是:函數

 

console.log(0.1+0.2);//輸出0.30000000000000004this

這對於浮點數的四則運算(加減乘除),幾乎全部的編程語言都會出現上面相似的精度偏差問題,只是大部分語言都處理封裝了避免偏差的方法。對於js而言,因爲它是一門弱類型的語言,因此並無對浮點數的運算有解決的封裝方法,這能咱們本身來解決。這裏爲何會出現這個精度偏差呢?prototype

 

浮點數產生的緣由  htm

咱們首先就想到計算機能讀懂的是二進制,因此咱們進行運算的時候,其實是把數字轉換爲了二進制進行的,因此咱們把0.1和0.2轉換爲二進制:blog

 

0.1 => 0.0001 1001 1001 1001..(無限循環)it

0.2 => 0.0011 0011 0011 0011…(無限循環)io

這裏能夠看出轉換爲二進制是一個無限循環的數字,單在計算機中對於無限循環的數字會進行舍入處理的,進行雙精度浮點數的小數部分最多支持52位。而後把兩個2進制的數進行運算得出的也是一個二進制數值,最後再把它轉換爲十進制。保留17位小數,因此0.1+0.2的值就成了 0.30000000000000004。  0.1+0.1的值成了0.20000000000000000,全是0的時候能夠省略,就成了0.2

 

解決浮點數精度偏差的辦法

最簡單的處理,經過toFixed方法,

console.log(parseFloat(0.1+0.2).toFixed(1));//輸出0.3

說明:經過toFixed(num)方法來保留小數,其中num爲保留小數的位數,這個方法是根據四捨五入來保留小數的,因此計算的結果並非最精確的。因此咱們須要採用其它方法來實現,經過Number.prototype的屬性進行添加,以下:

 

js加法:

//加法函數

function accAdd(arg1, arg2) {

       var r1, r2, m;

       try {

              r1 = arg1.toString().split(".")[1].length;

       } catch(e) {

              r1 = 0;

       }

       try {

              r2 = arg2.toString().split(".")[1].length;

       }catch(e){

              r2 = 0;

       }

       m = Math.pow(10, Math.max(r1, r2));

       return(arg1 * m + arg2 * m) / m;

} //給Number類型增長一個add方法,使用時直接用 .add 便可完成計算。  

Number.prototype.add = function(arg){     

       return accAdd(arg, this); 

};

console.log(0.1.add(0.2).add(0.3));//等價於0.1+0.2+0.3,輸出0.6

console.log(0.1+0.2+0.3);//輸出0.6000000000000001

 

js減法:

//減法函數 

function Subtr(arg1, arg2) {

       var r1, r2, m, n;

       try {

              r1 = arg1.toString().split(".")[1].length;

       } catch(e) {

              r1 = 0;

       }

       try {

              r2 = arg2.toString().split(".")[1].length;

       } catch(e) {

              r2 = 0;

       }

       m = Math.pow(10, Math.max(r1, r2)); //動態控制精度長度            

       n = (r1 >= r2) ? r1 : r2;

       return parseFloat(((arg1 * m - arg2 * m) / m).toFixed(n));

}

Number.prototype.sub = function(arg) {

       return Subtr(this, arg);

};

console.log(0.6.sub(0.2).sub(0.3));//等價於0.6-0.2-0.3 輸出0.1

console.log(0.6-0.2-0.3);//輸出:0.09999999999999998

 

js乘法:

//乘法函數

function accMul(arg1, arg2) {

       var m = 0,

              s1 = arg1.toString(),

              s2 = arg2.toString();

       try {

              m += s1.split(".")[1].length;

       } catch(e) {}

       try {

              m += s2.split(".")[1].length;

       } catch(e) {}

       return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);

}

Number.prototype.mul = function (arg) {     

       return accMul(arg, this); 

};

console.log(0.1.mul(0.2).mul(0.3)); //等價於0.1 * 0.2 * 0.3 輸出0.006

console.log(0.1 * 0.2 * 0.3); //輸出:0.006000000000000001

 

js除法:

//除法函數

function accDiv(arg1, arg2) {

       var t1 = 0,

              t2 = 0,

              r1, r2;

       try {

              t1 = arg1.toString().split(".")[1].length;

       } catch(e) {}

       try {

              t2 = arg2.toString().split(".")[1].length;

       } catch(e) {}

       with(Math) {

              r1 = Number(arg1.toString().replace(".", ""));

              r2 = Number(arg2.toString().replace(".", ""));

              return(r1 / r2) * pow(10, t2 - t1);

       }

}

Number.prototype.div = function (arg) {     

       return accDiv(this, arg); 

};

console.log(0.6.div(0.2).div(0.1)); //等價於0.6 / 0.2 / 0.1 輸出30

console.log(0.6 / 0.2 / 0.1); //輸出:29.999999999999993

 

原文出處:https://www.cnblogs.com/ranyonsue/p/11378200.html

相關文章
相關標籤/搜索