2011-03-30 137 views
6

JavaScript的「Number.toFixed」的默認實現似乎有點失敗。破壞到固定實現

console.log((8.555).toFixed(2)); // returns 8.56 
console.log((8.565).toFixed(2)); // returns 8.57 
console.log((8.575).toFixed(2)); // returns 8.57 
console.log((8.585).toFixed(2)); // returns 8.59 

我需要一個比這更一致的舍入方法。

在8.500和8.660之間的範圍內,下列數字不能正確舍入。

8.575 
8.635 
8.645 
8.655 

我試着修復原型實現如下,但它只是在那裏的一半。任何人都可以提出任何可以使其工作更一致的改變嗎?

Number.prototype.toFixed = function(decimalPlaces) { 
    var factor = Math.pow(10, decimalPlaces || 0); 
    var v = (Math.round(this * factor)/factor).toString(); 
    if (v.indexOf('.') >= 0) { 
     return v + factor.toString().substr(v.length - v.indexOf('.')); 
    } 
    return v + '.' + factor.toString().substr(1); 
}; 
+1

我可以想象這是一個浮點精度錯誤。 – 2011-03-30 18:46:12

+0

[http://stackoverflow.com/questions/566564/javascript-functions-math-round0-vs-tofixed0-and-browser-inconsistencies](http://stackoverflow.com/questions/566564/javascript-functions-math -round0-vs-tofixed0和瀏覽器不一致)類似? – Prescott 2011-03-30 18:55:09

+0

我剛剛在Chromium V12.0上試過上面的例子,並收到以下結果:8.55 8.56 8.57 8.59。所以你的里程可能會有所不同,這取決於你的JavaScript實現。 – HBP 2011-03-30 19:10:51

回答

5

這是因爲浮點錯誤的。

比較(8.575).toFixed(20)(8.575).toFixed(3)並想象這個命題:8.575 < real("8.575"),其中real是一個虛構函數,創建一個無限精度的實數。

也就是說,原來的數字不是預期的和不準確已被引入。

我能想到的一個快速「workabout」是:乘以1000(或酌情),得到toFixed(0)(仍然有一個限制,但它是荒謬的),然後推回小數形式。

快樂編碼。

+0

自然,我不能在我的覆蓋相同的功能中使用「toFixed」。我不得不堅持使用「Math.round」,但是你的回答指向了正確的方向。 Ty – Joshua 2011-03-30 19:24:08

+0

@Joshua:是的,你可以。只需將一個toFixed'的副本存儲在一個變量中,然後將其應用於該數字。 – Eric 2011-03-30 19:29:31

7

感謝您的回答pst。我的實現幾乎可以工作,但在某些情況下並沒有出現浮點錯誤。

這條線在我的函數是罪魁禍首: Math.round(此*因子)

(它是在Number.prototype,所以「本」是多少); 8.575 * 100出現在857.4999999999999,然後輪流下來。 這是通過改變要讀取行如下校正: Math.round(Math.round(這*因子* 100)/ 100)

我的整個解決方法現在變爲:

Number.prototype.toFixed = function(decimalPlaces) { 
    var factor = Math.pow(10, decimalPlaces || 0); 
    var v = (Math.round(Math.round(this * factor * 100)/100)/factor).toString(); 
    if (v.indexOf('.') >= 0) { 
     return v + factor.toString().substr(v.length - v.indexOf('.')); 
    } 
    return v + '.' + factor.toString().substr(1); 
}; 
0

Check my answer

function toFixed(num, precision) { 
    return (+(Math.round(+(num + 'e' + precision)) + 'e' + -precision)).toFixed(precision); 
} 
0

也許這將幫助別人,這是固定的流行formatMoney()函數,但正確的圓角。

Number.prototype.formatMoney = function() { 
    var n = this, 
    decPlaces = 2, 
    decSeparator = ",", 
    thouSeparator = " ", 
    sign = n < 0 ? "-" : "", 
    i = parseInt(n = Math.abs(+n || 0)) + "", 
    j = (j = i.length) > 3 ? j % 3 : 0, 
    decimals = Number(Math.round(n +'e'+ decPlaces) +'e-'+ decPlaces).toFixed(decPlaces), 
    result = sign + (j ? i.substr(0, j) + thouSeparator : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thouSeparator) + (decPlaces ? decSeparator + Math.abs(decimals-i).toFixed(decPlaces).slice(2) : ""); 
    return result; 
}; 

(9.245).formatMoney(); // returns 9,25 
(7.5).formatMoney(); // returns 7,50 
(8.575).formatMoney(); // returns 8,58