解决JS加减乘除四则运算结果不准确以及精度丢失的问题

问题描述

在做公司库存数量的逻辑时踩到的大坑,如果对于浮点数,直接在JavaScript中进行加减乘除四则运算会出现很大的误差。问题产生的原因在于,计算机中是将十进制的数据转化为二进制来进行计算的,而对于浮点型的数据,转化成二进制之后可能会变成一个无限循环的数字,而计算机中是不允许无限循环的存在的,那就要做截断,而截断后的数据运算之后再转成十进制就可能会产生极大的误差。

其实并不是只JS存在这种问题,Java、C++也都如此,只不过他们内部都有封装好的方法来解决这个问题。JS是一门弱语言,没有做类似的操作。

解决办法

看到网上也有用toFixed()方法来解决的,但是这种四舍五入的方式在高精度需求下也显得不那么严谨。

最常用的方法就是把需要计算的数字升级(乘以10的n次幂)成计算机能够精确识别的整数,等计算完毕再降级(除以10的n次幂),这是大部分编程语言处理精度差异的通用方法。 

参考代码

(Ant Design for Vue语言情景下)

          /**
           * arg1与arg2相加
           */
          accAdd(arg1, arg2) {
            var 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};
            m = Math.pow(10, Math.max(r1, r2));
            return (arg1 * m + arg2 * m) / m;
          },
          /**
           * arg1与arg2相减
           */
          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);
          },
          /**
           * arg1与arg2相乘
           */
          accMul(arg1, arg2) {
            var 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);
          },
          /**
           * arg1与arg2相除,并以四舍五入的方式保留小数点后2位
           */
          divide(arg1, arg2) {
            var d1, d2,
              n1 = Number(arg1.toString().replace(".", "")),
              n2 = Number(arg2.toString().replace(".", ""));
            try {d1 = arg1.toString().split(".")[1].length;} catch (e) {d1 = 0;}
            try {d2 = arg2.toString().split(".")[1].length;} catch (e) {d2 = 0;}
            return this.toFixed((n1 / n2) * Math.pow(10, d2 - d1), 2);
          },

JS中自带的toFixed函数会把多余的小数直接截掉,因此,这里进行重写,采用四舍五入的方式保留小数 。

          /**
           * arg以四舍五入的方式保留小数点后n位
           */
          toFixed(arg, n) {
            if (n == 0) {
              return Math.round(arg)
            }
            try {
              var d, carryD, i,
                ds = arg.toString().split('.'),
                len = ds[1].length,
                b0 = '', k = 0
              if (len > n) {
                while (k < n && ds[1].substring(0, ++k) == '0') {
                  b0 += '0'
                }
                d = Number(ds[1].substring(0, n))
                carryD = Math.round(Number('0.' + ds[1].substring(n, len)))
                len = (d + carryD).toString().length
                if (len > n) {
                  return Number(ds[0]) + 1
                } else if (len == n) {
                  return Number(ds[0] + '.' + (d + carryD))
                }
                return Number(ds[0] + '.' + b0 + (d + carryD))
              }
            } catch (e) {
            }
            return arg
          },

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值