Open wusb opened 6 years ago
由于计算机二进制环境下浮点数的计算精度缺失,导致我们用JS进行数据计算的时候会出现很多意想不到的情况:
console.log(70-67.9); //2.0999999999999943
我们预期的结果是2.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。
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为要保留的小数位数
此方法简单快速,在数据处理比较少的地方很实用。
由于计算机二进制环境下浮点数的计算精度缺失,导致我们用JS进行数据计算的时候会出现很多意想不到的情况:
我们预期的结果是2.1,结果却来了这么一大串…,为了达到预期的结果,我们可以采用以下解决方案:
toFixed看似完美的解决了我们的问题,其实,坑才刚刚挖好:
整数也有小数点
虽然是保留一位小数,但是3.0这种显示结果并不优雅,不过问题不大,简单处理一下即可:保留几(n)位小数我们就乘10n。
属于银行家舍入法
我们先看现状
虽然并不完全符合银行家舍入法的规则(不符合的原因应该是二进制下浮点数的坑导致的),但toFixed所存在的问题已经找到了。
重写toFixed方法解决以上两个问题:
下面我们来验证结果:
调用重写方法,可以一劳永逸的解决toFixed存在的问题。当然,我们也有更直接的方法来处理,见方法2。
Math.round() 四舍五入取整
比如,0.015(num)需要保留两(n)位小数,可先将这个数乘以100(10*n),四舍五入取整后,再除以100(10*n)。
此方法简单快速,在数据处理比较少的地方很实用。