例子:0.1 + 0.2 != 0.3
一、原因
计算机是通过二进制的方式存储数据的,所以计算机计算 0.1 + 0.2 时,实际上计算的是两个数的二进制的和。
0.1的二进制是 0.0001100110011001100...(1100循环)
0.2的二进制是 0.001100110011001100...(1100循环)
这两个数的二进制都是无限循环的数。
number类型相当于其他强类型语言中的double类型(双精度浮点型),不区分浮点型和整数型。由于Js的所有数字类型都是双精度浮点型(64位)采用 IEEE754 标准,最大可以存储53位有效数字,53位后面的会遵循“0舍1入”的原则。
根据这个原则,0.1和0.2的二进制数相加,再转换为十进制数就是:0.30000000000000004
所以0.1 + 0.2 != 0.3
二、解决方案
方案1:Number.EPSILON
设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2^52,在ES6中,提供了Number.EPSILON属性,而它的值就是2^52,只要判断 0.1 + 0.2 - 0.3 是否小于Number.EPSILON,如果小于,则 0.1 + 0.2 == 0.3
function isEqual(a, b) {
return Math.abs(a - b) < Number.EPSILON
}
方案2:转为字符串
function addStrings(num1, num2) {
let i = num1.length - 1;
let j = num2.length - 1;
const result = [];
let carry = 0;
while (i >= 0 || j >= 0) {
const n1 = i > 0 ? Number(num[i]) : 0
const n2 = j > 0 ? Number(num[j]) : 0
const sum = n1 + n2 + carry;
result.unshift(sum % 10);
carry = Math.floor(sum / 10);
i--;
j--;
}
if(carry) {
result.unshift(carry);
}
retuen result.join('');
}
function isEqual(a, b, sum) {
const [intStr1, deciStr1] = a.toString().split('.');
const [intStr2, deciStr2] = b.toString().split('.');
const intSum = addStrings()(intStr1, intStr2); // 获取整数相加部分
const deciSum = addStrings(deciStr1, deciStr2); // 获取小数相加部分
return intSum + '.' + deciSum === String(sum)
}
实际开发场景中,判等场景较少,多数为两个数字直接运算
// 加法
function accAdd(arg1, arg2) {
let 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 }
// 当 4个两位小数的的值相加时,仍然又精度问题,处理方式:讲数值先乘以 m,再除以m
m = Math.pow(10, Math.max(r1, r2) + 2)
console.log('selectedRowKeys changed: ', m)
return (arg1 * m + arg2 * m) / m
}
// 减法
function accSubtr(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 ((arg1 * m - arg2 * m) / m).toFixed(n);
}
// 乘法
function accMul(arg1, arg2) {
if (typeof (arg1) !== 'undefined' && typeof (arg2) !== 'undefined') {
let 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)
}
}
// 除法
funcion accDiv(arg1, arg2) {
let t1 = 0, t2 = 0, r1, r2;
try { t1 = arg1.toString().split(".")[1].length } catch (e) { }
try { t2 = arg2.toString().split(".")[1].length } catch (e) { }
if (Math) {
r1 = Number(arg1.toString().replace(".", ""))
r2 = Number(arg2.toString().replace(".", ""))
return (r1 / r2) * Math.pow(10, t2 - t1);
}
}