昨天工作中发现了一个问题,是后端计算的成交价和前端计算的成交价有时候会有一分钱的差异,后来发现是由于 toFixed 的方法存在差异。
在 C# 中的取舍方法使用的是银行家舍入法,也就是四舍六入五取偶(又称四舍六入五留双)。在 javaScript 中 Number.prototype.toFixed()的方法在四舍和六入上没什么争议,而当判断位为5的时候就显得有点奇怪。
银行家舍入法
据说,大部分的编程软件都使用的是这种方法,也算是一种国际标准。 所谓银行家舍入法,其实质是一种四舍六入五取偶(又称四舍六入五留双)法。其规则是:当舍去位的数值小于5时,直接舍去该位;当舍去位的数值大于等于6时,在舍去该位的同时向前位进一;当舍去位的数值等于5时,如果前位数值为奇,则在舍去该位的同时向前位进一,如果前位数值为偶,则直接舍去该位。
以下是各个浏览器中测试 toFixed() 的结果:
Chrome:
FireFox:
IE:
可以看出在不同浏览器中,toFixed方法都给出了不同的结果,让人摸不到头脑。
于是我查询了 ECMA 里Number.prototype.toFixed() 的规范,如下:
拿 0.15 和 10.15 举个栗子。
(0.15).toFixed(1)
num = 0.15; f = 1;
根据步骤 10.a :
2 ÷ 10^f - num // 0.05000000000000002
1 ÷ 10^f - num // -0.04999999999999999
取最接近 0 的值,得 (0.15).toFixed(1) 返回 0.1。
(10.15).toFixed(1)
num = 10.15; f = 1;
根据步骤 10.a :
102 ÷ 10^f - num // 0.049999999999998934
101 ÷ 10^f - num // -0.05000000000000071
取最接近 0 的值,得 (10.15).toFixed(1) 返回 10.2。
归根到底就是浮点数精度的锅。
找到原因后,我想到了两种解决方案:
使用Math.round()
用这个方法可以实现传统的四舍五入。
重写Number.prototype.toFixed()
这个方法则是更加公平的四舍六入五取偶。
重写后的结果: