js处理浮点数计算误差
众所周知, 浮点计算会产生舍入误差的问题, 比如, 0.1 + 0.2, 结果应该是0 .3, 但是计算的结果并不是如此, 而是0 .30000000000000004
我们知道,能被计算机读懂的是二进制,而不是十进制,所以我们先把 0.1 和 0.2 转换成二进制:
0.1==》0.1.toString(2)==》0.0001100110011(无限循环..)
0.2==》0.2.toString(2)==》0.001100110011 (无限循环..)
双精度浮点数的小数部分最多支持 52 位,所以两者相加之后得到这么一串 0.0100110011001100110011001100110011001100110011001100 因浮点数小数位的限制而截断的二进制数字,这时候,我们再把它转换为十进制,就成了 0.30000000000000004。
网上流行一种写法:
就是先乘以一个倍数,将其变为整数,再相加,再除以倍数的方法,这个方法乍一看,是行的通的,但是经过测试,此方法也会存在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); //位数差的绝对值
m = Math.pow(10, Math.max(r1, r2)); //较大数的幂
if (c > 0) { //位数相差
var cm = Math.pow(10, c);
if (r1 > r2) {
arg1 = Number(arg1.toString().replace(".", "")); //转化成数字
arg2 = Number(arg2.toString().replace(".", "")) * cm;
} else {
arg1 = Number(arg1.toString().replace(".", "")) * cm;
arg2 = Number(arg2.toString().replace(".", ""));
}
} else { //位数相等
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
与第一种方法不同的是 不是每一个加数都乘以倍数,而是比较加数小数点后的位数,位数大的转为字符串,然后用空替换掉小数点再转为number类型,小数点后位数小的去乘以乘以倍数,两者相加再除以倍数就可以得到准确值;
其他 减 乘除 类似