关于js中小数运算丢失精度的处理办法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于js中小数运算丢失精度的处理办法相关的知识,希望对你有一定的参考价值。

参考技术A 浮点数值的最高精度是17位小数,但在进行运算的时候其精确度却远远不如整数;整数在进行运算的时候都会转成10进制; 而java和javascript中计算小数运算时,都会先将十进制的小数换算到对应的二进制,一部分小数并不能完整的换算为二进制,这里就出现了第一次的误差。待小数都换算为二进制后,再进行二进制间的运算,得到二进制结果。然后再将二进制结果换算为十进制,这里通常会出现第二次的误差。

所以(0.1+0.2)!=03

解决这种问题,可以将小数变成整数进行运算,然后再将结果变为小数。

//乘法

function multiNum (a,b)

  var c = 0,

d = a.toString(),

e = b.toString();

try

      c += d.split(".")[1].length;

catch (f)

  try

      c += e.split(".")[1].length;

catch (f)

  return Number(d.replace(".","")) * Number(e.replace(".","")) / Math.pow(10,c);



//除法

function divide (a,b)

  var c,d,e = 0,

f = 0;

try

      e = a.toString().split(".")[1].length;

catch (g)

  try

      f = b.toString().split(".")[1].length;

catch (g)

  return c = Number(a.toString().replace(".","")),d = Number(b.toString().replace(".","")),this.mul(c / d,Math.pow(10,f - e));



//加法

function addNum (a,b)

  var c,d,e;

try

      c = a.toString().split(".")[1].length;

catch (f)

      c = 0;



  try

      d = b.toString().split(".")[1].length;

catch (f)

      d = 0;



  return e = Math.pow(10,Math.max(c,d)),(multiNum(a,e) + multiNum(b,e)) / e;



//减法

function subNum (a,b)

  var c,d,e;

try

      c = a.toString().split(".")[1].length;

catch (f)

      c = 0;



  try

      d = b.toString().split(".")[1].length;

catch (f)

      d = 0;



  return e = Math.pow(10,Math.max(c,d)),(multiNum(a,e) - multiNum(b,e)) / e;

关于js浮点数计算精度不准确问题的解决办法

今天在计算商品价格的时候再次遇到js浮点数计算出现误差的问题,以前就一直碰到这个问题,都是简单的使用tofixed方法进行处理一下,这对于一个程序员来说是及其不严谨的。因此在网上收集了一些处理浮点数精度的文章。觉得别人写的挺好了,我在简单的总结一下,以方便后续查阅。

浮点数误差产生的原因:

先看一个实例:

0.1 + 0.2 =?

0.1 + 0.2 = 0.3?

我们先来看一段 JS。

console.log( 0.1+ 0.2);

输出为 0.30000000000000004。是不是很奇葩
其实对于浮点数的四则运算,几乎所有的编程语言都会有类似精度误差的问题,只不过在 C++/C#/Java 这些语言中已经封装好了方法来避免精度的问题,而 JavaScript 是一门弱类型的语言,从设计思想上就没有对浮点数有个严格的数据类型,所以精度误差的问题就显得格外突出。下面就分析下为什么会有这个精度误差,以及怎样修复这个误差。

首先,我们要站在计算机的角度思考 0.1 + 0.2 这个看似小儿科的问题。我们知道,能被计算机读懂的是二进制,而不是十进制,所以我们先把 0.1 和 0.2 转换成二进制看看:

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

上面我们发现0.1和0.2转化为二进制之后,变成了一个无限循环的数字,这在现实生活中,无限循环我们可以理解,但计算机是不允许无限循环的,对于无限循环的小数,计算机会进行舍入处理。进行双精度浮点数的小数部分最多支持 52 位,所以两者相加之后得到这么一串 0.0100110011001100110011001100110011001100110011001100 因浮点数小数位的限制而截断的二进制数字,这时候,我们再把它转换为十进制,就成了 0.30000000000000004。

知道了浮点数产生的原因了,那么怎么处理这个问题呢?

方法一:指定要保留的小数位数(0.1+0.2).toFixed(1) = 0.3;这个方法toFixed是进行四舍五入的也不是很精准,对于计算金额这种严谨的问题,不推荐使用,而且不通浏览器对toFixed的计算结果也存在差异。

方法二:把需要计算的数字升级(乘以10的n次幂)成计算机能够精确识别的整数,等计算完毕再降级(除以10的n次幂),这是大部分编程语言处理精度差异的通用方法。 

 

eg:(0.1*10 + 0.2*10) / 10 == 0.3 // true

 

网上的处理方法:

 

//加法   
    Number.prototype.add = function(arg){   
        var r1,r2,m;   
        try{r1=this.toString().split(".")[1].length}catch(e){r1=0}   
        try{r2=arg.toString().split(".")[1].length}catch(e){r2=0}   
        m=Math.pow(10,Math.max(r1,r2))   
        return (this*m+arg*m)/m   
    }  
    //减法   Number.prototype.sub = function (arg){   
    return this.add(-arg);   
}   

//乘法   Number.prototype.mul = function (arg)   {   
    var m=0,s1=this.toString(),s2=arg.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.div = function (arg){   
    var t1=0,t2=0,r1,r2;   
    try{t1=this.toString().split(".")[1].length}catch(e){}   
    try{t2=arg.toString().split(".")[1].length}catch(e){}   
    with(Math){   
        r1=Number(this.toString().replace(".",""))   
        r2=Number(arg.toString().replace(".",""))   
        return (r1/r2)*pow(10,t2-t1);   
    }   
}

参考文章:http://div.io/topic/1349

             http://rockyee.iteye.com/blog/891538

            http://www.zhoulujun.cn/zhoulujun/html/theory/computBase/2016_0714_7860.html#announ

 

以上是关于关于js中小数运算丢失精度的处理办法的主要内容,如果未能解决你的问题,请参考以下文章

数值金额计算js封装--包含加减乘除四个方法,能确保浮点数运算不丢失精度

js javascript 四舍五入结果不正确 不对

2019-06-12 关于微服务大数字精度丢失解决方案

JS位运算异常(位运算精度丢失)的原因探究

前端获取Long类型精度丢失解决办法

js解决数字运算丢失精度,保留小数位