Javascript篇章二:js计算精度丢失

js精确度丢失出现缘由

1.我们先看几个四舍五入的实例

alert(Number(0.009).toFixed(2));  
alert(Number(162.295).toFixed(2)); 

    按正常结果,应该分别弹出0.01和162.30。但实际测试结果却是在不同浏览器中得到的是不同的结果:
    在ie6、7、8下得到0.00和162.30,第一个数截取不正确;
    在firefox中得到0.01和162.29,第二个数截取不正确;
    在opera下得到0.01和162.29,第二个数截取不正确

2.再看有关于四则运算的实例

alert(1/3);//弹出: 0.3333333333333333  
alert(0.1 + 0.2);//弹出: 0.30000000000000004   
alert(-0.09 - 0.01);//弹出: -0.09999999999999999  
alert(0.012345 * 0.000001);//弹出: 1.2344999999999999e-8  
alert(0.000001 / 0.0001);//弹出: 0.009999999999999998  

    按正常结果,除第一行外(因为其本身就不能除尽),其他都应该要得到精确的结果,从弹出的结果我们却发现不是我们想要的正确结果。是因为没有转换成Number类型吗?我们转换成Number后再计算看看:

alert(Number(1)/Number(3));//弹出: 0.3333333333333333       
alert(Number(0.1) + Number(0.2));//弹出: 0.30000000000000004      
alert(Number(-0.09) - Number(0.01));//弹出: -0.09999999999999999     
alert(Number(0.012345) * Number(0.000001));//弹出: 1.2344999999999999e-8     
alert(Number(0.000001) / Number(0.0001));//弹出: 0.009999999999999998  

    还是一样的结果,看来javascript默认把数字识别为number类型。为了验证这一点,我们用typeof弹出类型看看:

alert(typeof(1));//弹出: number  
alert(typeof(1/3));//弹出: number  
alert(typeof(-0.09999999));//弹出: number  

3.计算机原理

    回忆一下大学时学过的计算机原理,计算机执行的是二进制算术,当十进制数不能准确转换为二进制数时,这种精度误差就在所难免。从上述的推演过程我们知道,这种误差是难免的,c#的decimal和Java的BigDecimal之所以没有出现精度差异,只是因为在其内部作了相应处理,把这种精度差异给屏蔽掉了,而javascript是一种弱类型的脚本语言,本身并没有对计算精度做相应的处理,这就需要我们另外想办法处理了

解决JS计算精确度丢失

    创建calc.js文件,其调用方法参考  thinkphp项目调用Vue扩展函数方式

参考文章

Vue.prototype.calc = {

    /**
     * 千分位格式化函数
     * @param {*} number         要格式化的数字
     * @param {*} decimals       保留几位小数
     * @param {*} dec_point      小数点符号
     * @param {*} thousands_sep  千分位符号
     */
    number_format(number, decimals, dec_point, thousands_sep) {
        if(parseFloat(number) == 0) return " - ";
        number = (number + '').replace(/[^0-9+-Ee.]/g, '');
        var n = !isFinite(+number) ? 0 : +number,
            decimals = (typeof decimals === 'undefined') ? 2 : decimals,
            prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
            sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
            dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
            s = '',
            toFixedFix = function (n, prec) {
                var k = Math.pow(10, prec);
                return '' + Math.ceil(n * k) / k;
            };
     
        s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
        var re = /(-?\d+)(\d{3})/;
        while (re.test(s[0])) {
            s[0] = s[0].replace(re, "$1" + sep + "$2");
        }
     
        if ((s[1] || '').length < prec) {
            s[1] = s[1] || '';
            s[1] += new Array(prec - s[1].length + 1).join('0');
        }
        return s.join(dec);
    },

    /**
     * 加法函数,用来得到精确的加法结果
     * 说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
     * 调用:accAdd(arg1,arg2)
     * 返回值:arg1加上arg2的精确结果
     **/
    accAdd: function(arg1, arg2) {
        var r1, r2, m, c;
        try {
            r1 = arg1.toString().split(".")[1].length;
        }
        catch (e) {
            r1 = 0;
        }
        try {
            r2 = arg2.toString().split(".")[1].length;
        }
        catch (e) {
            r2 = 0;
        }
        c = Math.abs(r1 - r2);
        m = Math.pow(10, Math.max(r1, r2));
        if (c > 0) {
            var cm = Math.pow(10, c);
            if (r1 > r2) {
                arg1 = Number(arg1.toString().replace(".", ""));
                arg2 = Number(arg2.toString().replace(".", "")) * cm;
            } else {
                arg1 = Number(arg1.toString().replace(".", "")) * cm;
                arg2 = Number(arg2.toString().replace(".", ""));
            }
        } else {
            arg1 = Number(arg1.toString().replace(".", ""));
            arg2 = Number(arg2.toString().replace(".", ""));
        }
        return (arg1 + arg2) / m;
    },

    /**
     * 减法函数,用来得到精确的减法结果
     * 说明:javascript的减法结果会有误差,在两个浮点数相减的时候会比较明显。这个函数返回较为精确的减法结果。
     * 调用:accSub(arg1,arg2)
     * 返回值:arg1加上arg2的精确结果
     **/
    accSub: function(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)); //last modify by deeka //动态控制精度长度
        n = (r1 >= r2) ? r1 : r2;
        return parseFloat(((arg1 * m - arg2 * m) / m).toFixed(n));
    },

    //乘法函数,用来得到精确的乘法结果 
    //说明:javascript的乘法结果会有误差,在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果。 
    //调用:accMul(arg1,arg2) 
    //返回值:arg1乘以arg2的精确结果 
    accMul: function(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);
    },

    //除法函数,用来得到精确的除法结果 
    //说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显。这个函数返回较为精确的除法结果。 
    //调用:accDiv(arg1,arg2) 
    //返回值:arg1除以arg2的精确结果 
    accDiv: function(arg1, arg2) {
        var t1 = 0,
            t2 = 0,
            r1, r2;
        try {
            t1 = arg1.toString().split(".")[1].length
        } catch (e) {}
        try {
            t2 = arg2.toString().split(".")[1].length
        } catch (e) {}
        with(Math) {
            r1 = Number(arg1.toString().replace(".", ""));
            r2 = Number(arg2.toString().replace(".", ""));
            return (r1 / r2) * pow(10, t2 - t1);
        }
    }
}

你可能感兴趣的:(JavaScript,javascript,vue.js,前端)