Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wusb opened this issue Feb 10, 2018 · 0 comments
Open

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

wusb opened this issue Feb 10, 2018 · 0 comments

Comments

@wusb
Copy link
Owner

wusb commented Feb 10, 2018

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

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

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

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

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

  • 整数也有小数点

    console.log((70.9-67.9).toFixed(1));   //3.0

    虽然是保留一位小数,但是3.0这种显示结果并不优雅,不过问题不大,简单处理一下即可:保留几(n)位小数我们就乘10n。

    console.log((70.9-67.9).toFixed(1)*10/10);   //3
  • 属于银行家舍入法

    银行家舍入:所谓银行家舍入法,其实质是一种四舍六入五取偶(又称四舍六入五留双)法。简单来说就是:四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。

    我们先看现状

    console.log(0.005.toFixed(2));   // 0.01 正确
    console.log(0.015.toFixed(2));   // 0.01 错误
    console.log(0.025.toFixed(2));   // 0.03 正确
    console.log(0.035.toFixed(2));   // 0.04 正确
    console.log(0.045.toFixed(2));   // 0.04 错误
    console.log(0.055.toFixed(2));   // 0.06 正确
    console.log(0.065.toFixed(2));   // 0.07 正确
    console.log(0.075.toFixed(2));   // 0.07 错误
    console.log(0.085.toFixed(2));   // 0.09 正确
    console.log(0.095.toFixed(2));   // 0.10 正确

    虽然并不完全符合银行家舍入法的规则(不符合的原因应该是二进制下浮点数的坑导致的),但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为要保留的小数位数

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant