wusb / blog

🖋 Personal blog, Welcome Fork, Watch & Star
http://simbawu.com
MIT License
50 stars 14 forks source link

JS 处理数据中的一些坑 #1

Open wusb opened 6 years ago

wusb commented 6 years ago

由于计算机二进制环境下浮点数的计算精度缺失,导致我们用JS进行数据计算的时候会出现很多意想不到的情况:

console.log(70-67.9);   //2.0999999999999943

我们预期的结果是2.1,结果却来了这么一大串…,为了达到预期的结果,我们可以采用以下解决方案:

  1. toFixed(1) 保留一位小数
console.log((70-67.9).toFixed(1));   //2.1

toFixed看似完美的解决了我们的问题,其实,坑才刚刚挖好:

重写toFixed方法解决以上两个问题:

Number.prototype.toFixed = function(length)
  {
    var carry = 0; //存放进位标志
    var num,multiple; //num为原浮点数放大multiple倍后的数,multiple为10的length次方
    var str = this + ''; //将调用该方法的数字转为字符串
    var dot = str.indexOf("."); //找到小数点的位置
    if(str.substr(dot+length+1,1)>=5) carry=1; //找到要进行舍入的数的位置,手动判断是否大于等于5,满足条件进位标志置为1
    multiple = Math.pow(10,length); //设置浮点数要扩大的倍数
    num = Math.floor(this * multiple) + carry; //去掉舍入位后的所有数,然后加上我们的手动进位数
    var result = num/multiple + ''; //将进位后的整数再缩小为原浮点数
    return result;
  }

下面我们来验证结果:

console.log(0.015.toFixed(2));   // 0.02 正确
console.log((70.9-67.9).toFixed(1));   //3 正确

调用重写方法,可以一劳永逸的解决toFixed存在的问题。当然,我们也有更直接的方法来处理,见方法2。

  1. Math.round() 四舍五入取整

    比如,0.015(num)需要保留两(n)位小数,可先将这个数乘以100(10*n),四舍五入取整后,再除以100(10*n)。

console.log(Math.round(0.015*100)/100);   //0.02
Math.round(num*(10*n))/(10*n);   //通用公式,n为要保留的小数位数

此方法简单快速,在数据处理比较少的地方很实用。