js處理浮點(diǎn)數(shù)計(jì)算誤差
眾所周知韩脑, 浮點(diǎn)計(jì)算會(huì)產(chǎn)生舍入誤差的問題密末, 比如, 0.1 + 0.2引矩, 結(jié)果應(yīng)該是0 .3烧给, 但是計(jì)算的結(jié)果并不是如此燕偶, 而是0 .30000000000000004
我們知道,能被計(jì)算機(jī)讀懂的是二進(jìn)制础嫡,而不是十進(jìn)制指么,所以我們先把 0.1 和 0.2 轉(zhuǎn)換成二進(jìn)制:
0.1==》0.1.toString(2)==》0.0001100110011(無限循環(huán)..)
0.2==》0.2.toString(2)==》0.001100110011 (無限循環(huán)..)
雙精度浮點(diǎn)數(shù)的小數(shù)部分最多支持 52 位,所以兩者相加之后得到這么一串 0.0100110011001100110011001100110011001100110011001100 因浮點(diǎn)數(shù)小數(shù)位的限制而截?cái)嗟亩M(jìn)制數(shù)字榴鼎,這時(shí)候伯诬,我們?cè)侔阉D(zhuǎn)換為十進(jìn)制,就成了 0.30000000000000004檬贰。
網(wǎng)上流行一種寫法:
就是先乘以一個(gè)倍數(shù)姑廉,將其變?yōu)檎麛?shù),再相加翁涤,再除以倍數(shù)的方法桥言,這個(gè)方法乍一看,是行的通的葵礼,但是經(jīng)過測(cè)試号阿,此方法也會(huì)存在bug。
下面是改良后直接上代碼
function accAdd(arg1, arg2) {
var r1, r2, m, c;
try {
r1 = arg1.toString().split(".")[1].length;
console.log(arg1.toString().split("."))
} catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split(".")[1].length;
} catch (e) {
r2 = 0;
}
c = Math.abs(r1 - r2); //位數(shù)差的絕對(duì)值
m = Math.pow(10, Math.max(r1, r2)); //較大數(shù)的冪
if (c > 0) { //位數(shù)相差
var cm = Math.pow(10, c);
if (r1 > r2) {
arg1 = Number(arg1.toString().replace(".", "")); //轉(zhuǎn)化成數(shù)字
arg2 = Number(arg2.toString().replace(".", "")) * cm;
} else {
arg1 = Number(arg1.toString().replace(".", "")) * cm;
arg2 = Number(arg2.toString().replace(".", ""));
}
} else { //位數(shù)相等
arg1 = Number(arg1.toString().replace(".", ""));
arg2 = Number(arg2.toString().replace(".", ""));
}
console.log(arg1 + arg2, arg1, arg2)
return (arg1 + arg2) / m;
}
let a = accAdd(0.1, 0.7);
console.log(a, 0.1 + 0.7) //0.8 0.7999999999999999
與第一種方法不同的是 不是每一個(gè)加數(shù)都乘以倍數(shù)鸳粉,而是比較加數(shù)小數(shù)點(diǎn)后的位數(shù)扔涧,位數(shù)大的轉(zhuǎn)為字符串,然后用空替換掉小數(shù)點(diǎn)再轉(zhuǎn)為number類型届谈,小數(shù)點(diǎn)后位數(shù)小的去乘以乘以倍數(shù)枯夜,兩者相加再除以倍數(shù)就可以得到準(zhǔn)確值;
其他 減 乘除 類似