javaScript 中 toFixed() 精度问题及解决方案

昨天工作中发现了一个问题,是后端计算的成交价和前端计算的成交价有时候会有一分钱的差异,后来发现是由于 toFixed 的方法存在差异。

在 C# 中的取舍方法使用的是银行家舍入法,也就是四舍六入五取偶(又称四舍六入五留双)。在 javaScript 中 Number.prototype.toFixed()的方法在四舍和六入上没什么争议,而当判断位为5的时候就显得有点奇怪。

银行家舍入法

据说,大部分的编程软件都使用的是这种方法,也算是一种国际标准。 所谓银行家舍入法,其实质是一种四舍六入五取偶(又称四舍六入五留双)法。其规则是:当舍去位的数值小于5时,直接舍去该位;当舍去位的数值大于等于6时,在舍去该位的同时向前位进一;当舍去位的数值等于5时,如果前位数值为奇,则在舍去该位的同时向前位进一,如果前位数值为偶,则直接舍去该位。

以下是各个浏览器中测试 toFixed() 的结果:

Chrome:

Chrome 下的 toFixed

FireFox:

FF 下的 toFixed

IE:

IE 下的 toFixed

可以看出在不同浏览器中,toFixed方法都给出了不同的结果,让人摸不到头脑。

于是我查询了 ECMA 里Number.prototype.toFixed() 的规范,如下:

 ECMA-262 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()

用这个方法可以实现传统的四舍五入。

通过 Math.round() 来实现传统四舍五入


重写Number.prototype.toFixed()

这个方法则是更加公平的四舍六入五取偶。

重写 Number.prototype.toFixed()

重写后的结果:

重写后在 Chrome 上的结果

你可能感兴趣的:(javaScript 中 toFixed() 精度问题及解决方案)