• 浮点数运算和金额处理


       

    做一个用户中奖记录页面,展示每条记录和总金额,遇到浮点数相加出现问题。

    Js的浮点数在比较或者做运算的时候会不准确,这个只在初学js的时候读到过概念性的东西,这次就遇到了就在这里记一笔,做一下相关拓展,网上处理的方法也很多,这里摘要一些博主的。

    浮点数运算:

    转自:https://blog.csdn.net/u013347241/article/details/79210840

    思路是将小数转成整数来运算,之后再转回小数。

          

    'use strict'
     
    var accAdd = function(num1, num2) {
        num1 = Number(num1);
        num2 = Number(num2);
        var dec1, dec2, times;
        try { dec1 = countDecimals(num1)+1; } catch (e) { dec1 = 0; }
        try { dec2 = countDecimals(num2)+1; } catch (e) { dec2 = 0; }
        times = Math.pow(10, Math.max(dec1, dec2));
        // var result = (num1 * times + num2 * times) / times;
        var result = (accMul(num1, times) + accMul(num2, times)) / times;
        return getCorrectResult("add", num1, num2, result);
        // return result;
    };
     
    var accSub = function(num1, num2) {
        num1 = Number(num1);
        num2 = Number(num2);
        var dec1, dec2, times;
        try { dec1 = countDecimals(num1)+1; } catch (e) { dec1 = 0; }
        try { dec2 = countDecimals(num2)+1; } catch (e) { dec2 = 0; }
        times = Math.pow(10, Math.max(dec1, dec2));
        // var result = Number(((num1 * times - num2 * times) / times);
        var result = Number((accMul(num1, times) - accMul(num2, times)) / times);
        return getCorrectResult("sub", num1, num2, result);
        // return result;
    };
     
    var accDiv = function(num1, num2) {
        num1 = Number(num1);
        num2 = Number(num2);
        var t1 = 0, t2 = 0, dec1, dec2;
        try { t1 = countDecimals(num1); } catch (e) { }
        try { t2 = countDecimals(num2); } catch (e) { }
        dec1 = convertToInt(num1);
        dec2 = convertToInt(num2);
        var result = accMul((dec1 / dec2), Math.pow(10, t2 - t1));
        return getCorrectResult("div", num1, num2, result);
        // return result;
    };
     
    var accMul = function(num1, num2) {
        num1 = Number(num1);
        num2 = Number(num2);
        var times = 0, s1 = num1.toString(), s2 = num2.toString();
        try { times += countDecimals(s1); } catch (e) { }
        try { times += countDecimals(s2); } catch (e) { }
        var result = convertToInt(s1) * convertToInt(s2) / Math.pow(10, times);
        return getCorrectResult("mul", num1, num2, result);
        // return result;
    };

    //计算小数位的长度
    var countDecimals = function(num) { var len = 0; try { num = Number(num); var str = num.toString().toUpperCase(); if (str.split('E').length === 2) { // scientific notation var isDecimal = false; if (str.split('.').length === 2) { str = str.split('.')[1]; if (parseInt(str.split('E')[0]) !== 0) { isDecimal = true; } } let x = str.split('E'); if (isDecimal) { len = x[0].length; } len -= parseInt(x[1]); } else if (str.split('.').length === 2) { // decimal if (parseInt(str.split('.')[1]) !== 0) { len = str.split('.')[1].length; } } } catch(e) { throw e; } finally { if (isNaN(len) || len < 0) { len = 0; } return len; } }; //将小数转成整数 var convertToInt = function(num) { num = Number(num); var newNum = num; var times = countDecimals(num); var temp_num = num.toString().toUpperCase(); if (temp_num.split('E').length === 2) { newNum = Math.round(num * Math.pow(10, times)); } else { newNum = Number(temp_num.replace(".", "")); } return newNum; }; //确认我们的计算结果无误,以防万一 var getCorrectResult = function(type, num1, num2, result) { var temp_result = 0; switch (type) { case "add": temp_result = num1 + num2; break; case "sub": temp_result = num1 - num2; break; case "div": temp_result = num1 / num2; break; case "mul": temp_result = num1 * num2; break; } if (Math.abs(result - temp_result) > 1) { return temp_result; } return result; };
     

       

    对金额的处理:

    转自: https://www.css88.com/archives/7324 

    格式化金额:

    /**
     * 将数值四舍五入(保留2位小数)后格式化成金额形式
     *
     * @param num 数值(Number或者String)
     * @return 金额格式的字符串,如'1,234,567.45'
     * @type String
     */
    function formatCurrency(num) {
        num = num.toString().replace(/$|\,/g,'');
        if(isNaN(num))
            num = "0";
        sign = (num == (num = Math.abs(num)));
        num = Math.floor(num*100+0.50000000001);
        cents = num%100;
        num = Math.floor(num/100).toString();
        if(cents<10)
        cents = "0" + cents;
        for (var i = 0; i < Math.floor((num.length-(1+i))/3); i++)
        num = num.substring(0,num.length-(4*i+3))+','+
        num.substring(num.length-(4*i+3));
        return (((sign)?'':'-') + num + '.' + cents);
    }

        或者

    function fmoney(s, n) {
        /*
         * 参数说明:
         * s:要格式化的数字
         * n:保留几位小数
         * */
        n = n > 0 && n <= 20 ? n : 2;
        s = parseFloat((s + "").replace(/[^d.-]/g, "")).toFixed(n) + "";
        var l = s.split(".")[0].split("").reverse(),
            r = s.split(".")[1];
        t = "";
        for (i = 0; i < l.length; i++) {
            t += l[i] + ((i + 1) % 3 == 0 && (i + 1) != l.length ? "," : "");
        }
        return t.split("").reverse().join("") + "." + r;
    }
    //调用
    fmoney(9.7,2);//9.70
    fmoney('12345.675910', 3);//12,345.676

       

        不过关系到经济利益的时候,还要考虑舍去或者舍入几厘。大家懂的,每个用户几厘钱可能带来巨大的经济收益。

    就比如说收手续费,如果一笔手续费计算出来是 3.4521 元,精确到分一般都会收 3.46 元。当然如果是付出去,那可能就是直接舍去了,一般会计算为 3.45 元。

    function number_format(number, decimals, dec_point, thousands_sep,roundtag) {
        /*
        * 参数说明:
        * number:要格式化的数字
        * decimals:保留几位小数
        * dec_point:小数点符号
        * thousands_sep:千分位符号
        * roundtag:舍入参数,默认 "ceil" 向上取,"floor"向下取,"round" 四舍五入
        * */
        number = (number + '').replace(/[^0-9+-Ee.]/g, '');
        roundtag = roundtag || "ceil"; //"ceil","floor","round"
        var n = !isFinite(+number) ? 0 : +number,
            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);
                console.log();
     
                return '' + parseFloat(Math[roundtag](parseFloat((n * k).toFixed(prec*2))).toFixed(prec*2)) / 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);
    }
    console.log(number_format(2, 2, ".", ","))//"2.00"
    console.log(number_format(3.7, 2, ".", ","))//"3.70"
    console.log(number_format(3, 0, ".", ",")) //"3"
    console.log(number_format(9.0312, 2, ".", ","))//"9.03"
    console.log(number_format(9.00, 2, ".", ","))//"9.00"
    console.log(number_format(39.715001, 2, ".", ",", "floor")) //"39.71"
    console.log(number_format(9.7, 2, ".", ","))//"9.70"
    console.log(number_format(39.7, 2, ".", ","))//"39.70"
    console.log(number_format(9.70001, 2, ".", ","))//"9.71"
    console.log(number_format(39.70001, 2, ".", ","))//"39.71"
    console.log(number_format(9996.03, 2, ".", ","))//"9996.03"
    console.log(number_format(1.797, 3, ".", ",", "floor"))//"1.797"

          

    推荐的类库

    Numeral.js

    一个用于格式化和操作数字的JavaScript库。数字可以被格式化为货币,百分比,时间,几个小数位数,千分位等等。您也可以随时创建自定义格式。

    官网及文档:http://numeraljs.com/

    GitHubhttps://github.com/adamwdraper/Numeral-js

       

    accounting.js

    一个轻量级的JavaScript库,用于格式化数字,金额和货币等。

    官网及文档:http://openexchangerates.github.io/accounting.js/

    GitHubaccounting.js

       

    Math.js

    Math.js 是专门为 JavaScript Node.js 提供的一个广泛的数学库。它具有灵活的表达式解析器,支持符号计算,配有大量内置函数和常量,并提供集成解决方案来处理不同的数据类型

    像数字,大数字(超出安全数的数字),复数,分数,单位和矩阵。功能强大,易于使用。

    官网:http://mathjs.org/

    GitHubhttps://github.com/josdejong/mathjs

       

    decimal.js

    JavaScript 提供十进制类型的任意精度数值。

    官网:http://mikemcl.github.io/decimal.js/

    GitHubhttps://github.com/MikeMcl/decimal.js

       

    big.js

    官网:http://mikemcl.github.io/big.js

    GitHubhttps://github.com/MikeMcl/big.js/

    这几个类库帮我们解决很多这类问题,不过通常我们前端做这类运算通常只用于表现层,应用并不是很多。所以很多时候,一个函数能解决的问题不需要引用一个类库来解决。

       

            

       

       

       

       

                     

  • 相关阅读:
    Stopping User Manager for UID 121” error
    IDEA——如何再次开启ESLint
    堆排序
    akka-typed(10)
    微慕WordPress小程序专业版v3.0发布
    微慕WordPress小程序增强版v2.0发布
    微慕WordPress小程序开源版v3.6发布
    微慕小程序开源版A标签优化说明
    微信小程序开放「分享到朋友圈」功能
    微信小程序直播安装和开发指南
  • 原文地址:https://www.cnblogs.com/yaoyao-sun/p/10274604.html
Copyright © 2020-2023  润新知