昨天工作中發現了一個問題,是后端計算的成交價和前端計算的成交價有時候會有一分錢的差異,后來發現是由于 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()
這個方法則是更加公平的四舍六入五取偶。
重寫后的結果: