在项目中计算商品价格的时候再次遇到js浮点数计算出现误差的问题,以前一碰到这个问题就用tofixed方法进行处理一下,这对于一个程序员来说是及其不严谨的。
何况用tofixed方法也是有问题的
一、精度问题案例
console.log(0.1 + 0.2);//0.30000000000000004
console.log(1.0 - 0.9);//0.09999999999999998
console.log(19.9 * 100);//1989.9999999999998
console.log(6.6 / 0.2);//32.99999999999999
二、不精准原因:
下面我们来说一下浮点数运算产生误差的原因:(拿0.1+0.2=0.30000000000000004进行举例)
首先,我们要站在计算机的角度思考 0.1 + 0.2 这个看似小儿科的问题。我们知道,能被计算机读懂的是二进制,而不是十进制,所以我们先把 0.1 和 0.2 转换成二进制看看:
0.1 => 0.0001 1001 1001 1001…(无限循环)
0.2 => 0.0011 0011 0011 0011…(无限循环)
上面我们发现0.1和0.2转化为二进制之后,变成了一个无限循环的数字,这在现实生活中,无限循环我们可以理解,但计算机是不允许无限循环的,对于无限循环的小数,计算机会进行舍入处理。进行双精度浮点数的小数部分最多支持 52 位,所以两者相加之后得到这么一串 0.0100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 ,因浮点数小数位的限制而截断的二进制数字,这时候,我们再把它转换为十进制,就成了 0.30000000000000004。
三、解决方案
// 计算补充0数量
export const getZero = (a, b) => {
let zero = '',
len = 0;
if (a > b) {
len = a - b;
} else {
len = b - a;
}
for (let i = 0; i < len; i++) {
zero += '0';
}
return zero;
};
// 浮点数转换整数
export const decimalToInteger = (a, b) => {
let _a = a.toString(),
_b = b.toString();
if (!_a.match(/\./g) && !_b.match(/\./g)) {
return {
_a,
_b,
e: 1
};
} else {
const al = _a.match(/\./g) ? _a.split('.')[1] : '',
bl = _b.match(/\./g) ? _b.split('.')[1] : '',
max = Math.max(al.length, bl.length),
e = Math.pow(10, max);
_b = _b.replace(/\./g, '');
_a = _a.replace(/\./g, '');
if (al.length > bl.length) {
_b += getZero(al.length, bl.length);
} else {
_a += getZero(al.length, bl.length);
}
return {
_a,
_b,
e
};
}
};
// 加
export const add = (a, b) => {
const {_a, _b, e} = decimalToInteger(a, b);
return (Number(_a) + Number(_b)) / e;
};
// 减
export const subtract = (a, b) => {
const {_a, _b, e} = decimalToInteger(a, b);
return (Number(_a) - Number(_b)) / e;
};
// 乘
export const multiply = (a, b) => {
const {_a, _b, e} = decimalToInteger(a, b);
return (Number(_a) * Number(_b)) / Math.pow(e,2);
};
// 除以
export const divide = (a, b) => {
const {_a, _b, e} = decimalToInteger(a, b);
return (Number(_a) / Number(_b));
};
四、调用方法
console.log(add(0.1, 0.2));//0.3
console.log(subtract(1.0, 0.9));//0.1
console.log(multiply(19.9, 100));//1990
console.log(divide(6.6, 0.2));//33`