js中toFixed精度問題的解決辦法


一:四舍五入並不是真正的四舍五入
這個問題是在測試階段我們的測試人員提出來的。一開始我也很吃驚,結果待我在控制台試了一些數據之后,我懵逼了,我一直在用的toFixed方法竟然有問題,我竟然糊塗的用它做了很多事!以下是我在chrome上的結果:

1.35.toFixed(1) // 1.4 正確
1.335.toFixed(2) // 1.33  錯誤
1.3335.toFixed(3) // 1.333 錯誤
1.33335.toFixed(4) // 1.3334 正確
1.333335.toFixed(5)  // 1.33333 錯誤
1.3333335.toFixed(6) // 1.333333 錯誤

果然有問題,只能網上找資料了,結果又發現同樣是上面的一段代碼,在IE下又小同大異,以下是IE上的結果:

1.35.toFixed(1) // 1.4 正確
1.335.toFixed(2) // 1.34  正確
1.3335.toFixed(3) // 1.334 正確
1.33335.toFixed(4) // 1.3334 正確
1.333335.toFixed(5)  // 1.33334 正確
1.3333335.toFixed(6) // 1.333334 正確

果然IE才是爸爸。難道是瀏覽器兼容性問題?兼容性問題難道不應該是出在IE中嗎?既然找到問題所在,就好下手。我的辦法是把要四舍五入的后一位單獨拎出來單獨判斷。

let result = number.toString();
const arr = result.split('.');
const integer = arr[0];
const decimal = arr[1];
result = integer + '.' + decimal.substr(0, n);
const last = decimal.substr(n, 1);

// 四舍五入,轉換為整數再處理,避免浮點數精度的損失

if (parseInt(last, 10) >= 5) {
    const x = Math.pow(10, n);
    result = ((parseFloat(result) * x) + 1) / x;
    result = result.toFixed(n);
}
return result;

自己測了幾遍,貌似沒什么問題,OK~
二:計算機二進制編碼導致的精度問題
沒過多久,測試又提出來頁面報錯了~~ 心塞啊,怎么可能報錯呢?自己debugger,發現頁面中的js進了死循環。很明顯問題出在toFixed中回調了toFixed,結果沒有走出來,繼續debugger,又有了進人的發現。以下是控制台測試:

console.log(2.115 * 100) // 211.50000000000003
console.log(2.0115 * 1000) // 2011.4999999999998

能告訴我是在鬧哪樣?好吧,我猜到了,肯定是計算機的進度問題。既然你一直進入循環,我就手動把你拉出來。

result = (Math.round((parseFloat(result)) * x) + 1) / x;

強制四舍五入取整,不會進死循環了!
以下是全部代碼:
// toFixed兼容方法

Number.prototype.toFixed = function (n) {
    if (n > 20 || n < 0) {
        throw new RangeError('toFixed() digits argument must be between 0 and 20');
    }
    const number = this;
    if (isNaN(number) || number >= Math.pow(10, 21)) {
        return number.toString();
    }
    if (typeof (n) == 'undefined' || n == 0) {
        return (Math.round(number)).toString();
    }

    let result = number.toString();
    const arr = result.split('.');

    // 整數的情況
    if (arr.length < 2) {
        result += '.';
        for (let i = 0; i < n; i += 1) {
            result += '0';
        }
        return result;
    }

    const integer = arr[0];
    const decimal = arr[1];
    if (decimal.length == n) {
        return result;
    }
    if (decimal.length < n) {
        for (let i = 0; i < n - decimal.length; i += 1) {
            result += '0';
        }
        return result;
    }
    result = integer + '.' + decimal.substr(0, n);
    const last = decimal.substr(n, 1);

    // 四舍五入,轉換為整數再處理,避免浮點數精度的損失
    if (parseInt(last, 10) >= 5) {
        const x = Math.pow(10, n);
        result = (Math.round((parseFloat(result) * x)) + 1) / x;
        result = result.toFixed(n);
    }

    return result;
};


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM