zoukankan      html  css  js  c++  java
  • js浮点数精度丢失问题及如何解决js中浮点数计算不精准

    js中进行数字计算时候,会出现精度误差的问题。先来看一个实例:

    console.log(0.1+0.2===0.3);//false

    console.log(0.1+0.1===0.2);//true

    上面第一个的输出会超出我们的常识,正常应该为true,这里为什么会是false呢,直接运行会发现0.1+0.2在js中计算的结果是:

    console.log(0.1+0.2);//输出0.30000000000000004

    这对于浮点数的四则运算(加减乘除),几乎所有的编程语言都会出现上面类似的精度误差问题,只是大部分语言都处理封装了避免误差的方法。对于js而言,由于它是一门弱类型的语言,所以并没有对浮点数的运算有解决的封装方法,这能我们自己来解决。这里为什么会出现这个精度误差呢?

    浮点数产生的原因 

    我们首先就想到计算机能读懂的是二进制,所以我们进行运算的时候,实际上是把数字转换为了二进制进行的,所以我们把0.1和0.2转换为二进制:

    0.1 => 0.0001 1001 1001 1001..(无限循环)

    0.2 => 0.0011 0011 0011 0011…(无限循环)

    这里可以看出转换为二进制是一个无限循环的数字,单在计算机中对于无限循环的数字会进行舍入处理的,进行双精度浮点数的小数部分最多支持52位。然后把两个2进制的数进行运算得出的也是一个二进制数值,最后再把它转换为十进制。保留17位小数,所以0.1+0.2的值就成了 0.30000000000000004。  0.1+0.1的值成了0.20000000000000000,全是0的时候可以省略,就成了0.2

    解决浮点数精度误差的办法

    最简单的处理,通过toFixed方法,

    console.log(parseFloat(0.1+0.2).toFixed(1));//输出0.3

    说明:通过toFixed(num)方法来保留小数,其中num为保留小数的位数,这个方法是根据四舍五入来保留小数的,所以计算的结果并不是最精确的。所以我们需要采用其它方法来实现,通过Number.prototype的属性进行添加,如下:

    js加法:

    //加法函数

    function accAdd(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(arg1 * m + arg2 * m) / m;

    } //给Number类型增加一个add方法,使用时直接用 .add 即可完成计算。  

    Number.prototype.add = function(arg){     

           return accAdd(arg, this); 

    };

    console.log(0.1.add(0.2).add(0.3));//等价于0.1+0.2+0.3,输出0.6

    console.log(0.1+0.2+0.3);//输出0.6000000000000001

    js减法:

    //减法函数 

    function Subtr(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 parseFloat(((arg1 * m - arg2 * m) / m).toFixed(n));

    }

    Number.prototype.sub = function(arg) {

           return Subtr(this, arg);

    };

    console.log(0.6.sub(0.2).sub(0.3));//等价于0.6-0.2-0.3 输出0.1

    console.log(0.6-0.2-0.3);//输出:0.09999999999999998

    js乘法:

    //乘法函数

    function accMul(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);

    }

    Number.prototype.mul = function (arg) {     

           return accMul(arg, this); 

    };

    console.log(0.1.mul(0.2).mul(0.3)); //等价于0.1 * 0.2 * 0.3 输出0.006

    console.log(0.1 * 0.2 * 0.3); //输出:0.006000000000000001

    js除法:

    //除法函数

    function accDiv(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);

           }

    }

    Number.prototype.div = function (arg) {     

           return accDiv(this, arg); 

    };

    console.log(0.6.div(0.2).div(0.1)); //等价于0.6 / 0.2 / 0.1 输出30

    console.log(0.6 / 0.2 / 0.1); //输出:29.999999999999993

  • 相关阅读:
    文件过滤驱动隐藏目标文件
    POJ 3345 Bribing FIPA(树形DP)
    POJ 1018 Communication System(分组背包DP)
    无用的,ring0暴力枚举进程模块
    HDOJ 3496 Watch The Movie(基本二维背包)
    栈回溯法的一个例子
    代码这样写奇丑无比...编码前期要做好规划工作啊
    多核发dpc安全inline hook
    纵我不往,知识不来学习Java第一周心得
    对“TD信息树”的使用体验
  • 原文地址:https://www.cnblogs.com/ranyonsue/p/11378200.html
Copyright © 2011-2022 走看看