JavaScript计算进度问题小计

十进制小数转为二进制小数方法

拿 9527.8125 举例如何将之转化为二进制小数

1.针对整数部分9527,采取除2取余,逆序排列

9527 / 2 = 4763 ... 1
4763 / 2 = 2381 ... 1
2381  / 2 = 1190 ... 1   ↑
1190 / 2 = 595 ... 0   | 逆序排列
595 / 2 = 297 ... 1    |
297  / 2 = 148 ... 1     |
148  / 2 = 74 ... 0
74 / 2 = 37 ... 1
37/ 2 = 18... 1
18 / 2 = 9 ... 1
9/ 2 = 4 ... 1
4 / 2 = 2 ... 0
2 / 2 = 1 ... 0
1 / 2 = 2 ... 1

 得整数部分的二进制为10011110110111.

2.针对小数部分0.8125,采取乘2取整,顺序排序

0.8125 * 2 = 1.625  |
0.625 * 2 = 1.25    | 顺序排列
0.25 * 2 = 0.5      |
0.5 * 2 = 1         ↓

 得小数部分的二进制为1001.

3.将前面两部分的结果相加,结果为10011110110111.1001;

小心,二进制小数会丢失了精度

根据上面的知识,将十进制0.1转为二进制;

0.1 * 2 = 0.2
0.2 * 2 = 0.4 // 注意这里
0.4 * 2 = 0.8
0.8 * 2 = 1.6
0.6 * 2 = 1.2
0.2 * 2 = 0.4 // 注意这里,循环开始
0.4 * 2 = 0.8
0.8 * 2 = 1.6
0.6 * 2 = 1.2
...

 可以发现有限十进制小数0.1转成无限二进制小数0.00011001100...,可以看到精度在转化过程中丢失了!

由此得出结论能转为有限二进制小数的最后一位必然以5结尾(因为只有0.5*2才能变成整数),其他结尾的都会丢失精度。

 推导 0.1 + 0.2 为何等于 0.30000000000000004

可以参考双精度浮点数

推荐阅读JavaScript浮点数陷阱及解法

阅读后得到以下公式

// 0.1 和 0.2 都转化成二进制后再进行运算
0.00011001100110011001100110011001100110011001100110011010 +
0.0011001100110011001100110011001100110011001100110011010 =
0.0100110011001100110011001100110011001100110011001100111

// 转成十进制正好是 0.30000000000000004

 解决方法小计1

//
Number.prototype['add'] = function (...arg) {
    var r1, r2, m, result = this;
    arg.forEach(value => {
        try { r1 = result.toString().split(".")[1].length } catch (e) { r1 = 0 }
        try { r2 = value.toString().split(".")[1].length } catch (e) { r2 = 0 }
        m = Math.pow(10, Math.max(r1, r2));
        result = Math.round(result * m + value * m) / m;
    });
    return result;
};
//
Number.prototype['sub'] = function (...arg) {
    var r1, r2, m, result = this;
    arg.forEach(value => {
        try { r1 = result.toString().split(".")[1].length } catch (e) { r1 = 0 }
        try { r2 = value.toString().split(".")[1].length } catch (e) { r2 = 0 }
        m = Math.pow(10, Math.max(r1, r2));
        var n = (r1 >= r2) ? r1 : r2;
        result = (Math.round(result * m - value * m) / m).toFixed(n);
    });
    return result;
};
//
Number.prototype['mul'] = function (...arg) {
    var result = this;
    arg.forEach(value => {
        var m = 0, s1 = result.toString(), s2 = value.toString();
        try { m += s1.split(".")[1].length } catch (e) { }
        try { m += s2.split(".")[1].length } catch (e) { }
        result = Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
    });
    return result;
};
//
Number.prototype['subtraction'] = function (...arg) {
    var result = this;
    arg.forEach(value => {
        var t1 = 0, t2 = 0, r1, r2;
        try { t1 = result.toString().split(".")[1].length } catch (e) { }
        try { t2 = value.toString().split(".")[1].length } catch (e) { }
        r1 = Number(result.toString().replace(".", ""));
        r2 = Number(value.toString().replace(".", ""));
        result = (r1 / r2) * Math.pow(10, t2 - t1);
    });
    return result;
};

 解决方法小计2

  • 传统使用,引入math.js
    <!DOCTYPE HTML>
    <html>
    <head>
      <script src="https://unpkg.com/mathjs@7.0.1/dist/math.min.js" type="text/javascript"></script>
    </head>
    <body>
      <script type="text/javascript">
        const ans = math.add(0.1, 0.2)     //  0.30000000000000004
        console.log(math.format(ans, {precision: 14})) // '0.3'
        console.log(math.sqrt(4).toString()) // 2
      </script>
    </body>
    </html>
  • es module
    	npm install mathjs
    
    import { create, all } from 'mathjs'
    const config = { 
      number: 'BigNumber',
      precision: 20
    }
    const math = create(all, config);
    export default {
      methods: {
          //开方
        numberSqrt: function(arg1){
            return math math.sqrt(arg1)
        },
        //
        numberExcept: function (arg1, arg2) {
          return math.divide(arg1, arg2);
        },
        //
        numberRide: function (arg1, arg2) {      
          return math.multiply(arg1, arg2);
        },
        //
        numberAdd:function (arg1,arg2) {
        return math.add(arg1, arg2);
        }
        //
        numberSub:function (arg1,arg2) {
        return math.add(arg1, -arg2);
        }
      }
    }
原文地址:https://www.cnblogs.com/wuxu-dl/p/15246472.html