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 加速服务