前言
在前端开发中,经常会遇到小数精度丢失的问题,比如,87.7,最终显示成87.69999999999.这种,这就是常见的精度丢失问题。这篇文章就是主要介绍精度丢失的问题。
出现小数精度丢失的原因
在js中数值型的数据都是number型的,不区浮点型还是整数型,而且js的所有的数字类型都是双精度浮点型(64位)采用IEEE754标准。
其中 64位 = 1位符号位 + 11位指数位 + 52位小数位
符号位:用来表示数字的正负,-1^符号位数值,0为正数,1为负数
-
指数位:一般都用科学计数法表示数值大小,但是这里一般都是2进制的科学计数法,表示2的多少次方
-
小数位:科学计数法前面的数值,IEEE745标准,默认所有的该数值都转为1.xxxxx这种格式,优点是可以省略一位小数位,可以存储更多的数字内容,缺点是丢失精度
浮点数的运算精度丢失问题就是因为,浮点数转化为该标准的二进制的过程中出现的丢失
JS可以存储的最大数字、最大安全数字
理论上:浮点数
查看方式 console.log(Number.MAX_VALUE) = 1.7976931348623157e+308 ,这个数就是最大数值
Number.MAX_SAFE_INTEGER = 2^53 -1 = 9007199254740991 最大安全数值
Number.MIN_SAFE_INTEGER = -(2^53-1) = -9007199254740991 最小安全数值
实际上:整数
整数 ±2^53 = 9007199254740992
基本上超过16位整数就无法精确地表示了 超过这个最大值,所有的奇数都会+1或者-1变成偶数,无法准确赋值
JS处理大数字的方法
- 结合第三方库,使用json-bigint 可以进行转换
npm i json-bigint
JSONbig.parse(data)
避免精度丢失的方法
- 将浮点数转成整数进行计算,计算完成后再转成浮点数。
1)判断obj是否为一个整数
export const isInteger = (obj) => {
return Math.floor(obj) === obj //向下取整就是为了让整数部分截取下来不变
}
2)将一个浮点数转成整数,返回整数和倍数
export const toInteger = (floatNum) => {
var ret = {times: 1, num: 0};
if (isInteger(floatNum)) {
ret.num = floatNum;
return ret
}
//1.//转字符串
var strfi = floatNum + '';
//2.//拿到小数点为
var dotPos = strfi.indexOf('.');
//3. //截取需要的长度
var len = strfi.substr(dotPos + 1).length;
//4.倍数就是长度的幂
var times = Math.pow(10, len);
var intNum = parseInt(floatNum * times , 10);
ret.times = times;
ret.num = intNum;
return ret
}
3)把小数放大为整数,进行算术运算,再缩小为小数
export const operation = (a, b, op) => {
var o1 = toInteger(a);
var o2 = toInteger(b);
var n1 = o1.num;
var n2 = o2.num;
var t1 = o1.times;
var t2 = o2.times;
var max = t1 > t2 ? t1 : t2;
var result = null;
switch (op) {
case 'add':
if (t1 === t2) { // 两个小数位数相同
result = n1 + n2
} else if (t1 > t2) { // o1 小数位 大于 o2
result = n1 + n2 * (t1 / t2)
} else { // o1 小数位 小于 o2
result = n1 * (t2 / t1) + n2
}
return result / max;
case 'subtract':
if (t1 === t2) {
result = n1 - n2
} else if (t1 > t2) {
result = n1 - n2 * (t1 / t2)
} else {
result = n1 * (t2 / t1) - n2
}
return result / max;
case 'multiply':
result = (n1 * n2) / (t1 * t2);
return result;
case 'divide':
result = (n1 / n2) * (t2 / t1);
return result
}
}