js中進行數字計算時候,會出現精度誤差的問題。先來看一個實例:
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.30000000000000004
這對於浮點數的四則運算(加減乘除),幾乎所有的編程語言都會出現上面類似的精度誤差問題,只是大部分語言都處理封裝了避免誤差的方法。對於js而言,由於它是一門弱類型的語言,所以並沒有對浮點數的運算有解決的封裝方法,這能我們自己來解決。這里為什么會出現這個精度誤差呢?
浮點數產生的原因
我們首先就想到計算機能讀懂的是二進制,所以我們進行運算的時候,實際上是把數字轉換為了二進制進行的,所以我們把0.1和0.2轉換為二進制:
0.1 => 0.0001 1001 1001 1001..(無限循環)
0.2 => 0.0011 0011 0011 0011…(無限循環)
這里可以看出轉換為二進制是一個無限循環的數字,單在計算機中對於無限循環的數字會進行舍入處理的,進行雙精度浮點數的小數部分最多支持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