JavaScript 中 Number.prototype.toFixed 方法五舍六入的問題

五舍六入的問題

JavaScript中 Number.prototype.toFixed 方法,能夠將數字保留指定位數的小數,最後一位小數經過其後面一位「四捨五入」獲得。這裏之因此要將「四捨五入」打上引號,是由於在 Chrome/Firefox 上,這個方法根據小數位數的不一樣(或整數部分是否爲0),表現的行爲多是四捨五入,也多是「五舍六入」。是的,你沒聽錯,「五舍六入」,看下下面這組 Chrome 上的例子:html

describe('toFixed() test', function () {
    it('Number.toFixed()', function () {
        expect(1.35.toFixed(1)).toBe("1.4");                      // true            
        expect(1.335.toFixed(2)).toBe("1.34");                  // false
        expect(1.3335.toFixed(3)).toBe("1.334");              // false
        expect(1.33335.toFixed(4)).toBe("1.3334");          // true
        expect(1.333335.toFixed(5)).toBe("1.33334");      // false
        expect(1.3333335.toFixed(6)).toBe("1.333334");  // false
    });
});

上面代碼中的有部分最後一個位是 5,卻被捨棄掉了,即「五舍六入」。這會致使頁面中,有些顯示小數的地方出現最後一位與預期不符的狀況。這種狀況在 IE6-IE10 上沒出現,可是在 Chrome44/Firefox41 上出現了。git

修復方式

這篇文章提供了兩種修復方法,一塊兒看一看:測試

第一種修復方式:判斷最後一位小數爲 5 的,改爲 6, 再調用 toFixed。例如 1.335(調用 toFixed(2) 結果爲 1.33)先修改成 1.336,在調用 toFixed(2) ,獲得的結果是 1.34,符合預期。this

function toFixed(num, fractionDigits) {
    var str = num + ''
    var len = str.length
    var last = str.substr(len-1, len)
    if (last == '5') {
        last = '6'
        str = str.substr(0, len-1) + last
        return Number(str).toFixed(fractionDigits)       
    } else {
        return num.toFixed(fractionDigits)
    }
}

第二種修復方式:將數字先進行放大指定倍數,使最後一位小數顯示成個位數,而後加上一個 0.5 後,使最後一位進位(若是最後一位大於等於 5),再調用 parsInt() 去尾並縮小相同倍數。例如 1.335 放大 100 倍後是 133.5,加上 0.5 後是 134.0,調用 parseInt() 獲得 134,再縮小 100 倍獲得 1.34,結果符合預期。prototype

function toFixed(num, fractionDigits) {
    var times = Math.pow(10, fractionDigits)
    var des = num * times + 0.5
    des = parseInt(des, 10) / times
    return String(des)
}

上面兩種方式均可以修復「五舍六入」的問題,可是實現方法和計算複雜度都稍大,下面我提供一種簡單的方法。code

第三種修復方式:在調用 toFixed() 方法前,先給數字補一個極小值,而後再調用 toFixed() 方法。例如 1.335 加上一個極小值(0.00000001)後是 1.33500001,而後調用 toFixed(2)獲得 1.34,結果符合預期。這個極小值只要不影響最後結果,能夠根據要保留的小數位數決定取多少,但也不能過小了(例如 1.3350000001.toFixed(2) 結果是 1.34,而 1.33500000000000001.toFixed(2) 結果是 1.33)。htm

function toFixed(num, fractionDigits) {
    return (num + 0.00000001).toFiexed(fractionDigits);
}

使用第三種方式修復後,再次運行測試代碼,均可以獲得正確的結果:blog

describe('toFixed() test', function () {
    it('myToFixed()', function () {
        var oldToFixed = Number.prototype.toFixed;
        // 這裏重寫原生方法是爲了方便比較,不推薦這樣作
        Number.prototype.toFixed = function (n) {                   
            return oldToFixed.call(this + 0.00000001, n);
        }
        expect(1.35.toFixed(1)).toBe("1.4");
        expect(1.335.toFixed(2)).toBe("1.34");
        expect(1.3335.toFixed(3)).toBe("1.334");
        expect(1.33335.toFixed(4)).toBe("1.3334");
        expect(1.333335.toFixed(5)).toBe("1.33334");
        expect(1.3333335.toFixed(6)).toBe("1.333334");
        Number.prototype.toFixed = oldToFixed;
    });
});

參考

相關文章
相關標籤/搜索