美文网首页
前端项目中我们可以怎么解决精度丢失问题

前端项目中我们可以怎么解决精度丢失问题

作者: T_guo | 来源:发表于2022-11-14 14:58 被阅读0次

    背景

    • 0.1 + 0.2 = 0.3,但是为什么在我们在浏览器的控制台中输出却是0.30000000000000004?
    console.log(0.3 - 0.1) // 0.19999999999999998
    console.log(0.1 * 0.2) // 0.020000000000000004
    console.log(0.3 / 0.1) // 2.9999999999999996
    
    • 原因
      因为计算机是通过二进制来进行计算的,即 0 和 1
      例如: 0.1 + 0.2 ,0.1表示为0.0001100110011001...,而0.2表示为0.0011001100110011...
      而在二进制中 1 + 1 = 10,所以 0.1 + 0.2 = 0.0100110011001100...
      转成10进制就近似表示为 0.30000000000000004

    解决方案(一)

    使用toFixed()<不推荐>
    0.123.toFixed(2) // '0.12'
    

    可以控制小数点后几位,如果为空的话会用0补充,返回一个字符串
    但是,在不同浏览器中得出的值可能不相同,且部分数字得不到预计的结果,并不是执行严格的四舍五入

    较为完整的实现
    • 加法
    function mathPlus(arg1, arg2) {
       let r1, r2, m;
    try {
        r1 = arg1.toString().split(".")[1].length; // 获取小数点后字符长度
    } catch (error) {
       r1 = 0; // 为整数状态,r1赋0
    }
    try {
       r2 = arg2.toString().split(".")[1].length;
    } catch (error) {
       r2 = 0;
    }
    m = Math.pow(10, Math.max(r1, r2)); // 确保所有参数都为整数
       return (arg1 * m + arg2 * m) / m;
    }
    mathPlus(0.1, 0.2); // 0.3
    mathPlus(1, 2); // 3
    
    • 减法
    function mathSubtract(arg1, arg2) {
       let r1, r2, m;
    try {
       r1 = arg1.toString().split(".")[1].length;
    } catch (error) {
       r1 = 0;
    }
    try {
       r2 = arg2.toString().split(".")[1].length;
    } catch (error) {
        r2 = 0;
    }
    m = Math.pow(10, Math.max(r1, r2));
       return ((arg1 * m - arg2 * m) / m);
    }
    mathSubtract(0.3, 0.1); // 0.2
    mathSubtract(3, 1); // 2
    
    • 乘法
    function mathMultiply(arg1, arg2) {
       let m = 0;
       let s1 = arg1.toString();
       let 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)
    );
    }
    mathMultiply(0.1, 0.2); // 0.02
    mathMultiply(1, 2); // 2
    
    • 除法
    function mathDivide(arg1, arg2) {
       let m1 = 0;
       let m2 = 0;
       let n1 = 0;
       let n2 = 0;
    try {
       m1 = arg1.toString().split('.')[1].length;
    } catch (e) {}
    try {
        m2 = arg2.toString().split('.')[1].length;
    } catch (e) {}
       n1 = Number(arg1.toString().replace('.', ''));
       n2 = Number(arg2.toString().replace('.', ''));
    /**
    * 将除法转换成乘法
    * 乘以它们的小数点后个数差
    */
    return mathMultiply(n1 / n2, Math.pow(10, m2 - m1));
    }
    // > 0.2 / 0.03 => 6.666666666666667
    mathDivide(0.2, 0.03); // 6.666666666666665
    mathDivide(0.3, 0.1); // 3
    mathDivide(3, 1); // 3
    

    解决方案(二)

    使用浏览器自带的处理方式:
    Number(new Intl.NumberFormat('en-US').format(0.1 + 0.2))
    

    参考来源

    引入第三方库

    站在前人的肩膀上,可以前进的更快。下面这些成熟的库封装了很多实用的函数,虽然部分函数可能永远不会用到
    Math.js

    介绍:功能强大,内置大量函数,体积较大
    Github地址:github.com/josdejong/m…
    star: 12.2k+

    decimal.js

    介绍:支持三角函数等,并支持非整数幂
    Github地址:github.com/MikeMcl/dec…
    star: 4.8k+

    big.js

    介绍:体积6k,提供了CDN
    Github地址:github.com/MikeMcl/big…
    star: 3.9k+

    number-precision

    介绍:体积很小,只有1k左右
    Github地址:github.com/nefe/number…
    star: 3.4k+

    鸣谢

    https://juejin.cn/post/7081805392786751525

    相关文章

      网友评论

          本文标题:前端项目中我们可以怎么解决精度丢失问题

          本文链接:https://www.haomeiwen.com/subject/bqplxdtx.html