toFixed 函數(shù)引起的 bug

轉(zhuǎn)自:掘金 - 紅塵煉心
https://juejin.cn/post/6927215610552123406
前言
某天,客服告訴我,有客戶投訴,說賬單金額數(shù)據(jù)統(tǒng)計不對,同時測試也反饋在IE11瀏覽器上又是正確的。
經(jīng)過排查發(fā)現(xiàn)是toFixed()引起的。
緣由
來看一下toFixed()在chrome、火狐、IE下的不同表現(xiàn)。
chrome:

火狐:

IE:

可以看到toFixed()的四舍五入在chrome、火狐上并不準(zhǔn)確。
而toFixed()在chrome、火狐上也并不是網(wǎng)上所說的用銀行家舍入法來進(jìn)行四舍五入的。
銀行家舍入法的規(guī)則是“四舍六入五考慮,五后非零就進(jìn)一,五后為零看奇偶,五前為偶應(yīng)舍去,五前為奇要進(jìn)一”。
例如銀行家舍入法在 (2.55).toFixed(1) = 2.5、(3.55).toFixed(1) = 3.5 上就不符合了。
那為什么會這樣呢,要從toFixed的定義說起,來看ecmascript 規(guī)范對toFixed的表述:

按上圖中的步驟來演示一下(2.55).toFixed(1) = 2.5的處理過程。
x為2.55,小于,f為1,要使準(zhǔn)確的數(shù)學(xué)值盡可能接近零,取n為25和n為26,

可以看到最接近零的應(yīng)該是 -0.04999... ,故n為25,那么m為25,k為2, 為1,故a為2,則b為5,所以(2.55).toFixed(1)的結(jié)果為2.5。可以看出(2.55).toFixed(1)的結(jié)果是2.5而不是2.6,是... 引起,而為什么不等于0.5,其原因和不等0.3是一樣,可以看我這篇專欄《非科班前端人的一道送命題:0.1+0.2 等于 0.3 嗎?》。
但是在IE瀏覽器中,執(zhí)行 和 的結(jié)果和在chrome和火狐瀏覽器中執(zhí)行的結(jié)果是一樣。這里只能推斷IE瀏覽器中定義的toFixed不符合ecmascript 規(guī)范,具體原因目前也不清楚,如果知道的同學(xué)可以在評論中留言,謝謝。
解決
假設(shè)要四舍五入的數(shù)字為number,要保留n位小數(shù),可以先用 ,然后用 Math.round()取整,最后在除去,間接實(shí)現(xiàn)四舍五入。另外toFixed()還有個自動補(bǔ)零的功能,也要實(shí)現(xiàn)一下,故下面簡單封裝了一個toFixed方法來實(shí)現(xiàn)四舍五入。
function toFixed(number, m) {
if (typeof number !== 'number') {
throw new Error("number不是數(shù)字");
}
let result = Math.round(Math.pow(10, m) * number) / Math.pow(10, m);
result = String(result);
if (result.indexOf(".") == -1) {
if(m != 0){
result += ".";
result += new Array(m + 1).join('0');
}
} else {
let arr = result.split('.');
if (arr[1].length < m) {
arr[1] += new Array(m - arr[1].length + 1).join('0')
}
result = arr.join('.')
}
return result
}- EOF -
