JS 四舍五入方法 和 toFixed()最全总结(包括toFixed()的坑以及四舍五入实现方法)
Bululublue 2024-08-07 10:35:02 阅读 88
问题描述
最近在针对前端四舍五入做优化,前人是使用toFixed()来解决的,但客户发现有些数据并不是我们理解意义的四舍五入法。
查阅资料得知,toFixed()实现的是银行家算法:即四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一
更新: 网上很多资料都说toFixed()遵循的是银行家算法,但查看ECMA官网的说明(下带有红框图),觉得toFixed()并不严格遵循银行家算法,根因分析是IEEE754标准的限制,因此实现“四舍六入五成双”的银行家舍入,就不能通过toFixed()来简单实现了,具体解决方案代码请移步点击跳转
举个例子:
<code>// toFixed()
// 银行家算法即:四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一
console.log((1.450001).toFixed(1)); // 1.5 // 五后非零就进一
console.log((1.45000).toFixed(1)); // 1.4 // 五后为零看奇偶, 五前为偶应舍去
console.log((1.55000).toFixed(1)); // 1.6 // 五前为奇要进一
本以为这样就是toFixed的规则了,但是当我测试下面数据的时候,发现了奇怪的事情:
console.log((1.885450001).toFixed(4)); // 五后非零就进一
console.log((1.88545000).toFixed(4)); // 五后为零看奇偶, 五前为偶应舍去
console.log((1.88555000).toFixed(4)); // 五前为奇要进一
结果如下:
可以发现,(1.88545000).toFixed(4)应该是符合”五前为偶应舍去“的规则,那结果应该为1.8854, 但结果却是1.8855,我不理解,但大受震撼。。。查询ECMA官网看到这样一句话:
解释:n为整数,对于一个精确的值,则有n/10^f - x尽可能的接近于0,如果有两个这样的n,得到的n/10^f - x的值一样,那么取最大的那个。
(啥呀这是)初看确实不太好理解,跟着我继续分析。
原因分析:
<code>我们从结果来看:(1.88545000).toFixed(4)应该是符合”五前为偶应舍去“的规则,那结果应该为1.8854, 但实际是1.8855。那咱们就带入这个官方公式,看看是个怎么情况!
定义以下变量,n1=18855, n2=18854,来看看那个n带入公式最接近0
let n1 = 18855;
let n2 = 18854;
let f = 4;
let x = 1.88545000;
// 为方便查看那个更接近0,使用绝对值判断
console.log(Math.abs(n1 / Math.pow(10, f) - x));
console.log(Math.abs(n2 / Math.pow(10, f) - x));
打印结果如下:
一看吓一跳,原来n1为18855时带入公式最接近0,也就是说18855/10^4 最接近 x=1.88545000,到此对于toFixed()的分析就完整了。(toFixed()还是慎用啊)
解决方案:
那如何达到我们从小到大所理解的“四舍五入”,使用Math.round()即可解决:
<code>roundUp(data, size){
let a = Math.pow(10, size);
let res = Math.round(data * a) / a;
return res;
}
以为这样就可以了吗,试试roundUp(-2.5, 0),得到结果却是-2而不是-3,这是因为round在刚好舍入的为5时,则舍入到相邻的在正方向的整数。解决方法如下:
先记录数值正负,保证round操作的是正数,在最后返回时,再进行修改正负
roundUp (data, size) {
let abs = 1;
if (data < 0) abs = -1;
const _data = Math.abs(data);
let factor = Math.pow(10, size);
let result = Math.round(_data * factor) / factor;
// 这里使用toFixed的原因是为了防止舍入后round自动去除多余0的情况,
//这样达不到要求的小数位数,例如Math.round(1.4101, 3)为1.41而不是1.410,
//因此使用toFixed的作用仅仅是补充缺失的0
return (Number(result) * abs).toFixed(size).toString();
}
总结: toFixed()由于浮点数规则的限制,不能够准确的达到四舍五入的效果,IEEE754标准是计算机浮点数的表示方法,这也恰恰解释了0.1 + 0.2 != 0.3的问题,所以涉及到前端四舍五入的问题时,尽量不要使用toFixed(),使用Math.round(),但要注意Math.round()对于负数浮点数的四舍五入问题(在解决方案里有相应解决方法)
已更新至公司知识库
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。