• js小数精度问题


    一、场景

    0.1 + 0.2 = 0.30000000000000004 
    1.5 - 1.2 = 0.30000000000000004
    19.9 * 100 = 1989.9999999999998
    0.3 / 0.1 = 2.9999999999999996
     
    二、原因
    js中的数字只有 Number这种类型,其存采用的64位双精度浮点数(1位符号位、11位指数位,52位小数位),如下:
    做运算操作时会将10进制小数转换为2进制小数,整数部分采用除2取余法,如下:
      
     
    小数部分采用的“乘2取整,顺序排位法”,以0.1为例,如下:
      
    问题是不可能乘以2后恰好为整数,此时就会导致无限循环,而存储结构中的尾数部分最多只能表示 52 位,超出的会被舍弃掉,所以机器中存储的就是一个近似值,这就是导致小数精度的问题所在
     
    三、解决办法
     
    思路1:缩放法
     
    整数不会有精度问题,因此先将小数放大为整数进行运算,运算完再将结果缩为小数。
     
    1.先找出小数位数最多的数字作为x(例:a为0.1,b为0.22,那么x就是2),然后每个数字都乘以10的x次方变为为整数,方法如下
     
     Number(`${n}e${x}`)  //n为需要变换的数字,e在计算机中表示底数为10,x为小数位数。 转换也可以写成n*Math.pow(10,x)
    

    计算完成后,加减法用结果除以10的x次方,将结果变为小数。

    result / Math.pow(10,x)

    乘法:

    result/Math.pow(10,x*2) //如果是3个数,则将2换为3,以此类推

    除法

    假设a和b同时都放大了10的x次方,那么商相当于没有放大(也就是放大了10^0 = 1),由于商可能为小数,因此可以利用除以一个数,等于乘以该数的导数这个公式,调用乘法再次进行转换。

    /**
     * 计算小数位数
    */
    function digitLength(num) {
       const arr = num.toString().split('.');
       return arr[1]!==undefined ? arr[1].length : 0;
    }
    /**
     * 乘法
     */
     function times(a,b) {
        const x = digitLength(a),y = digitLength(b);
        const max = Math.max(x,y);
        const int_a = Number(`${a}e${max}`);
        const int_b = Number(`${b}e${max}`);
        const result = int_a * int_b;
        return result / Math.pow(10,x*2);
    }
    /**
     * 除法
     */
     function divide(a,b) {
        const x = digitLength(a),y = digitLength(b);
        const max = Math.max(x,y);
        const int_a = Number(`${a}e${max}`);
        const int_b = Number(`${b}e${max}`);
        const result = int_a / int_b;
        return times(result, 1); //result可能为小数,因此使用乘法,1的倒数还是1
    }
    

      

    思路2:

    使用toFixed()降低小数精度

    其他:

    使用mathjs函数库

  • 相关阅读:
    ES6的reduce( )方法 可以数组求和、数组去重、二维数组转一维数组、计算数组中每个元素出现的次数
    防止/禁止页面后退
    CSS预处理器SASS将迁移到Dart Sass
    FormData 对象的使用
    javascript中编码与解码的decodeURI()、decodeURIComponent()区别
    Chrome谷歌浏览器调试技巧小结
    javaweb:tomcat&servlet
    java:Druil连接池
    java:c3p0连接池的使用
    java:JDBC的使用方式
  • 原文地址:https://www.cnblogs.com/94pm/p/15990601.html
Copyright © 2020-2023  润新知