?JavaScript浮点数运算时经常出现精度异常问题,比如:0.1+0.2 !=0.3
当计算机计算?0.1+0.2?的时候,实际上计算的是这两个数字在计算机里所存储的二进制,0.1?和?0.2?在转换为二进制表示的时候会出现位数无限循环的情况。js 中是以?64?位双精度格式来存储数字的,只有?53?位的有效数字,超过这个长度的位数会被截取掉这样就造成了精度丢失的问题。这是第一个会造成精度丢失的地方。在对两个以?64?位双精度格式的数据进行计算的时候,首先会进行对阶的处理,对阶指的是将阶码对齐,也就是将小数点的位置对齐后,再进行计算,一般是小阶向大阶对齐,因此小阶的数在对齐的过程中,有效数字会向右移动,移动后超过有效位数的位会被截取掉,这是第二个可能会出现精度丢失的地方。当两个数据阶码对齐后,进行相加运算后,得到的结果可能会超过?53?位有效数字,因此超过的位数也会被截取掉,这是可能发生精度丢失的第三个地方。
对于这样的情况,我们可以将其转换为整数后再进行运算,运算后再转换为对应的小数,以这种方式来解决这个问题。
处理方案:
1.自定义函数处理
/**
* js数值计算 示例:funCalc([1.02,3,4])
* @param {*} arrList 参数数组
* @param {*} operator 操作符:1加法 2乘法
* @param {*} precision 精度 默认2位
*/
function funCalc(arrList, operator = 1, precision = 2) {
if (arrList.length === 0) return 0;
// 计算最大数度值,避免精度丢失,先扩大位数,再缩小
let mPow = Math.pow(10, precision); // 默认2位小数
let total = 0;
if (operator == 2) {
total = 1;
mPow = 1;
}
for (const item of arrList) {
let tem = 0;
if (item) {
tem = parseFloat(item) ?? 0; // parseFloat处理,转化失败时给0
tem = !isNaN(tem) ? tem : 0; // 处理 NaN 情况
}
switch (operator) {
case 1:
total += tem * mPow;
break;
case 2:
total *= tem * mPow;
break;
default:
total += tem * mPow;
}
}
// toFixed部分浏览器会用问题,toFixed它是一个四舍六入五成双的诡异的方法(也叫银行家算法),"四舍六入五成双"含义:对于位数很多的近似数,当有效位数确定后,其后面多余的数字应该舍去,只保留有效数字最末一位,这种修约(舍入)规则是“四舍六入五成双”,也即“4舍6入5凑偶”这里“四”是指≤4 时舍去,"六"是指≥6时进上,"五"指的是根据5后面的数字来定,当5后有数时,舍5入1;当5后无有效数字时,需要分两种情况来讲:①5前为奇数,舍5入1;②5前为偶数,舍5不进。(0是偶数)
const res = (total / mPow).toFixed(precision);
// // 修复toFixed
// const res = parseInt(total + 0.5, 10) / times;
return res;
}
?2.引入第三方库 bignumber.js?
BigNumber.js 支持很多相关计算,如四则运算、数据格式化、比较等,如果复杂的计算推荐使用。
官网地址:bignumber.js API
CDN引用地址:bignumber.js (v9.0.2) - A library for arbitrary-precision decimal and non-decimal arithmetic | BootCDN - Bootstrap 中文网开源项目免费 CDN 加速服务
|