js 精度问题的坑

坑1:含有小数的数字相加

例如 0.1+0.2 !==0.3

0.1+0.2
0.30000000000000004

因为在计算过程中是转成二进制的,

0.1.toString(2)
"0.0001100110011001100110011001100110011001100110011001101"
0.2.toString(2)
"0.001100110011001100110011001100110011001100110011001101"

双精度浮点数的小数部分最多支持 52 位,所以两者相加之后得到这么一串 0.0100110011001100110011001100110011001100110011001100 因浮点数小数位的限制而截断的二进制数字,这时候,我们再把它转换为十进制,就成了 0.30000000000000004

网友提供的处理思路:乘以10的N次方,在截取出整数部分进行相加处理,再除以10的N次方。

于是实现代码如下:

/** 含有小数点的数字 加法计算 */
  Number_Add(p_numberArray: number[], p_fixed: number = 3): number {
    let m = Math.pow(10, p_fixed);
    let result: number = 0;
    p_numberArray.map(x => {
      result += parseInt(String((x ?? 0) * m), 10);
    })
    return result / m;
  }

坑2:再chrome浏览器下的四舍五入

0.15.toFixed(1)
"0.1"
0.25.toFixed(1)
"0.3"
0.05.toFixed(1)
"0.1"
0.015.toFixed(2)
"0.01"

在精度的下一位如果数字是5,则会出现诡异的一幕,据说是银行家舍入法。

银行家舍入:所谓银行家舍入法,其实质是一种四舍六入五取偶(又称四舍六入五留双)法。

简单来说就是:四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。但是不论引入toFixed解决浮点数计算精度缺失的问题也好,它有没有使用银行家舍入法也罢,都是为了解决精度的问题,但是又离不开二进制浮点数的环境,但至少他帮助我们找到了问题所在,从而让我们有解决方法。

但我觉得不是。不过无所谓,能实现我们要的效果就行了。

 /** 精确到小数点几位 */
  NumberFixed(p_number: number, p_fixed: number = 2): number {
    try {
      if (typeof p_number === 'number') {
        //toFixed 方法有bug 应该抛弃
        let strNumber = p_number.toString();
        let strNumberArray = strNumber.split('.');
        if (strNumberArray.length == 1) {
          return Number(p_number.toFixed(p_fixed));
        }
        //decimalNubmer:小数点后面的数
        let decimalNubmer = strNumberArray[1];
        if (decimalNubmer.length > p_fixed) {
          let saveNumber = decimalNubmer.substring(0, p_fixed);
          let checkNumber = decimalNubmer.substr(p_fixed, 1);
          let result: number;
          if (Number(checkNumber) >= 5)
            result = Number(`${strNumberArray[0]}.${Number(saveNumber) + 1}`);
          else
            result = Number(`${strNumberArray[0]}.${saveNumber}`);
          return result;
        }
      }
      return p_number;
    }
    catch (ex) {
      console.log(`NumberFixed_error:p_number:${p_number},${ex}`);
    }
  }

后来我又找到了一个好的方案,以下是在nestjs环境下的方法:

/** 四舍五入 */
export function NumberFixed(p_number: number, p_fixed: number = 2): number {
  let tmpNumber = Math.pow(10, p_fixed)
  return Math.round((p_number + Number.EPSILON) * tmpNumber) / tmpNumber;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值