zoukankan      html  css  js  c++  java
  • js运算精度丢失问题

    0.0023 * 100 = 0.23,但js运算会出现下面这种精度丢失问题:

    直接参考别人写的方法:【中间处理了一些问题,最后有优化后的运算方法

    // 加
    function floatAdd(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 (floatMultiply(arg1 , m) + floatMultiply(arg2 , m)) / m;
    }
     
    // 减
    function floatSub(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 ((floatMultiply(arg1 , m) - floatMultiply(arg2 , m)) / m).toFixed(n);
    }
     
    // 乘
    function floatMultiply(arg1, arg2) {
        if(arg1 == null || arg2 == null){
            return null;
        }
        var n1,n2;
        var r1, r2; // 小数位数
        try {
            r1 = arg1.toString().split(".")[1].length;
        } catch (e) {
            r1 = 0;
        }
        try {
            r2 = arg2.toString().split(".")[1].length;
        } catch (e) {
            r2 = 0;
        }
        n1 = Number(arg1.toString().replace(".", ""));
        n2 = Number(arg2.toString().replace(".", ""));
        return n1 * n2 / Math.pow(10, r1+r2);
    }
     
    // 除
    function floatDivide(arg1, arg2) {
        if(arg1 == null){
            return null;
        }
        if(arg2 == null || arg2 == 0){
            return null;
        }
        var n1,n2;
        var r1, r2; // 小数位数
        try {
            r1 = arg1.toString().split(".")[1].length;
        } catch (e) {
            r1 = 0;
        }
        try {
            r2 = arg2.toString().split(".")[1].length;
        } catch (e) {
            r2 = 0;
        }
        n1 = Number(arg1.toString().replace(".", ""));
        n2 = Number(arg2.toString().replace(".", ""));
        return (n1 / n2) * Math.pow(10, r2 - r1);
    }

    上面运算方法存在的问题:

    1、floatDivide( 0.0033 , 100 ) = 0.000032999999999999996结果出现精度丢失问题:

      

     解决:除法运算的最后,需要使用乘法的方法,而不能直接相乘:

       

    2、floatDivide( 170890000 , 380026238.96999997 ) 得到的结果和实际数出入很大,如下图:

      

     测试:

      

      

           由上面两种情况猜测是乘法里面的科学计数法的问题:

       

     解决:添加将科学计数法转为字符串的方法,得到的结果就正常了,看下图:

       

        下面是测试的代码:

            var toNonExponential = (num)=> {
                    var m = num.toExponential().match(/d(?:.(d*))?e([+-]d+)/);
                    return num.toFixed(Math.max(0, (m[1] || '').length - m[2]));
                }
                /**
                 * 乘法 - js运算精度丢失问题
                 * @param arg1  数1
                 * @param arg2  数2
                 */
                var floatMultiply = (arg1, arg2) => {
                    if (arg1 == null || arg2 == null) {
                        return null;
                    }
                    arg1 = toNonExponential(arg1);
                    arg2 = toNonExponential(arg2);
                    var n1, n2;
                    var r1, r2; // 小数位数
                    try {
                        r1 = arg1.toString().split(".")[1].length;
                    } catch (e) {
                        r1 = 0;
                    }
                    try {
                        r2 = arg2.toString().split(".")[1].length;
                    } catch (e) {
                        r2 = 0;
                    }
                    n1 = Number(arg1.toString().replace(".", ""));
                    n2 = Number(arg2.toString().replace(".", ""));
                    return n1 * n2 / Math.pow(10, r1 + r2);
                }var floatDivide = (arg1, arg2) => {
                    if (arg1 == null) {
                        return null;
                    }
                    if (arg2 == null || arg2 == 0) {
                        return null;
                    }
                    var n1, n2;
                    var r1, r2; // 小数位数
                    try {
                        r1 = arg1.toString().split(".")[1].length;
                    } catch (e) {
                        r1 = 0;
                    }
                    try {
                        r2 = arg2.toString().split(".")[1].length;
                    } catch (e) {
                        r2 = 0;
                    }
                    n1 = Number(arg1.toString().replace(".", ""));
                    n2 = Number(arg2.toString().replace(".", ""));
                    return floatMultiply((n1 / n2), Math.pow(10, r2 - r1));
                    // return (n1 / n2) * Math.pow(10, r2 - r1);   // 直接乘法还是会出现精度问题
                }
                console.log(floatMultiply(4.496794759834739e-9,100000000));
                // console.log( floatDivide(170890000,380026238.9699997) );
                // console.log( floatDivide(170890000,380026238.969999) );
                // console.log( floatDivide(170890000,380026238.96999) );
    优化后的运算方法整合:
    /**
     * 将科学计数法的数字转为字符串  
     * 4.496794759834739e-9  ==> 0.000000004496794759834739
     * 4.496794759834739e+9  ==> 4496794759.834739
     * @param  num 
     */
    var toNonExponential = (num)=> {
        if(num == null) {
            return num;
        }
        if(typeof num == "number") {
            var m = num.toExponential().match(/d(?:.(d*))?e([+-]d+)/);
            return num.toFixed(Math.max(0, (m[1] || '').length - m[2]));
        }else {
            return num;
        }
    }
    
    /**
     * 乘法 - js运算精度丢失问题
     * @param arg1  数1
     * @param arg2  数2
     */
    var floatMultiply = (arg1, arg2) => {
        if (arg1 == null || arg2 == null) {
            return null;
        }
        arg1 = toNonExponential(arg1);
        arg2 = toNonExponential(arg2);
        var n1, n2;
        var r1, r2; // 小数位数
        try {
            r1 = arg1.toString().split(".")[1].length;
        } catch (e) {
            r1 = 0;
        }
        try {
            r2 = arg2.toString().split(".")[1].length;
        } catch (e) {
            r2 = 0;
        }
        n1 = Number(arg1.toString().replace(".", ""));
        n2 = Number(arg2.toString().replace(".", ""));
        return n1 * n2 / Math.pow(10, r1 + r2);
    }
    
    /**
     * 除法 - js运算精度丢失问题
     * @param arg1  数1
     * @param arg2  数2
     */
    var floatDivide = (arg1, arg2) => {
        if (arg1 == null) {
            return null;
        }
        if (arg2 == null || arg2 == 0) {
            return null;
        }
        arg1 = toNonExponential(arg1);
        arg2 = toNonExponential(arg2);
        var n1, n2;
        var r1, r2; // 小数位数
        try {
            r1 = arg1.toString().split(".")[1].length;
        } catch (e) {
            r1 = 0;
        }
        try {
            r2 = arg2.toString().split(".")[1].length;
        } catch (e) {
            r2 = 0;
        }
        n1 = Number(arg1.toString().replace(".", ""));
        n2 = Number(arg2.toString().replace(".", ""));
        return floatMultiply((n1 / n2), Math.pow(10, r2 - r1));
        // return (n1 / n2) * Math.pow(10, r2 - r1);   // 直接乘法还是会出现精度问题
    }
    
    /**
     * 加法 - js运算精度丢失问题
     * @param arg1  数1
     * @param arg2  数2
     */
    var floatAdd = (arg1, arg2) => {
        arg1 = toNonExponential(arg1);
        arg2 = toNonExponential(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 (floatMultiply(arg1, m) + floatMultiply(arg2, m)) / m;
    }
    
    /**
     * 减法 - js运算精度丢失问题
     * @param arg1  数1
     * @param arg2  数2
     */
    var floatSub = (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 ((floatMultiply(arg1, m) - floatMultiply(arg2, m)) / m).toFixed(n);
    }


  • 相关阅读:
    Server Tomcat v8.0 Server at localhost was unable to start within 45 seconds. If the server requires more time, try increasing the timeout in the server editor.
    用户画像——“打标签”
    python replace函数替换无效问题
    python向mysql插入数据一直报TypeError: must be real number,not str
    《亿级用户下的新浪微博平台架构》读后感
    【2-10】标准 2 维表问题
    【2-8】集合划分问题(给定要分成几个集合)
    【2-7】集合划分问题
    【2-6】排列的字典序问题
    【2-5】有重复元素的排列问题
  • 原文地址:https://www.cnblogs.com/stella1024/p/11905773.html
Copyright © 2011-2022 走看看