js计算的大坑

110 阅读3分钟

js开发周期很短所以天然的带有很多特性

0.1 + 0.2 不等于 0.3       // 0.30000000000000004
0.3 -0.1 不等于 0.2    // 0.19999999999999998
0.1 * 0.2 不等于0.02       //0.020000000000000004

js是有精度一说

浏览器处理不了太大的数据,范围是2的平方到52次方之间;例如16个9是和10000000000000001相等

同时,浏览器也存在处理小数不准确的事实。这是因为计算机只识别二进制数据,而且只能显示一定的有限长度。

小数精度常见场景:购物车里物品的价格计算与显示,在不同浏览器或设备上显示价格不一样,可能就是精度的问题。

解决方案:toFixed后再Number一下就可以了。

js计算是个巨坑

console.log('9' + -3)
//结果是字符串的‘9-3’

除了书写规范外,咱们最好先isNaN后再Number一下,再来进行运算。

js计算是个坑但世界业务中会遇到很多的js例如js的浮点数运算

0.1+0.2的返回结果不是0.3,而是0.30000000000000004。

这究竟是啥原因?
1,首先要肯定的是这不是浏览器的问题,而是由于浮点数的精度导致的。
2,浮点数的精度问题并不是JavaScript所独有的,例如在java中,我们运行以下代码,同样会输出和JavaScript中一样的结果。

double dd = 0.1+0.2;
System.out.println(dd);//0.30000000000000004?

why?
因为电脑永远都是按照二进制进行运算的,我们输入的十进制数在转化为二进制数时,并不总如人意,意思是说有些十进制并不能用准确的二进制数表示。

十进制小树转二进制表示

我们来看下十进制小树转二进制表示的过程,小数位乘以2,取整,小数部分继续乘以2,再取整,直到小数部分为0为止,然后将取整位按顺序排列。

由此方法我们可以看到以下一些十进制小数的二进制表示。

 十进制     二进制
 0.1        0.0001 1001 1001 1001 ...
 0.2        0.0011 0011 0011 0011 ...
 0.3        0.0100 1100 1100 1100 ...
 0.4        0.0110 0110 0110 0110 ...
 0.5        0.1
 0.6        0.1001 1001 1001 1001 ...

我们发现有些小数的二进制表示位数是无限循环的,这就造成了浮点数精度上的问题。

比如十进制的1.1,在用二进制表示的时因为存在二进制位无限循环的表示,实际值为1.0999999999...无限接近与1.1

解决方案

常用的一种解决方法就是将浮点数转化为整数进行运算,以下是一些封装好的关于浮点运算的方法,而且经过测试,加减乘除运算都会达到自己想要的结果。

 //求和
 function add(num1,num2){
     var r1,r2,m;
     try{
         r1 = num1.toString().split('.')[1].length;
     }catch(e){
         r1 = 0
     }
     try{
         r2 = num2.toString().split('.')[1].length;
     }catch(e){
         r2 = 0
     }
     m = Math.pow(10,Math.max(r1,r2));
     return Math.round(num1*m + num2*m)/m;
 }
//相减
 function sub(num1,num2){
     var r1,r2,m;
    try{
        r1 = num1.toString().split('.')[1].length;
    }catch(e){
        r1 = 0;
    }
    try{
        r2 = num1.toString().split('.')[1].length;
    }catch(e){
        r2 = 0;
    }
    m = Math.pow(10,Math.max(r1,r2));
    n = (r1 >= r2) ? r1 : r2;
    return (Math.round(num1 * m - num2*m) / m).toFixed(n);
 }
//相乘
 function mul(num1,num2){
     var m = 0,r1,r2;
     var s1 = num1.toString();
     var s2 = num2.toString();
     try{
         m += s1.split('.')[1].length
     }catch(e){      }
     try{}catch(e){
         m += s2.split('.')[1].length
     }catch(e){      }
     r1 = Number(num1.toString().replace(".",""));
     r2 = Number(num2.toString().replace(".",""));
     return r1 * r2 / Math.pow(10,m);
 }
 //相除
 function accDiv(){
      var t1,t2,r1,r2;
     try{
         t1 = num1.toString().split('.')[1].length;
     }catch(e){
         t1 = 0;
     }
     try{}catch(e){
         t2 = num2.toString().split('.')[1].length;
     }catch(e){
         t2 = 0;
     }
     r1 = Number(num1.toString().replace(".",""));
     r2 = Number(num2.toString().replace(".",""));
     return (r1 / r2)*Math.pwo(10,t2-t1);
 }