[Javascript] Math.round(num)與num.toFixed(0)和瀏覽器不一致


Answers

我認為FF與toFixed是正確的,因為下面的第10步說:“如果有兩個這樣的n,選擇較大的n”。

正如Grant Wagner所說:使用Math.ceil(x)Math.floor(x)而不是x.toFixed()

以下全部來自ECMAScript語言規範

15.7.4.5 Number.prototype.toFixed (fractionDigits)

返回一個字符串,其中包含以小數點後的小數點位數表示的定點符號表示的數字。 如果fractionDigits未定義,則假定為0 。 具體來說,執行以下步驟:

  1. fToInteger(fractionDigits) 。 (如果fractionDigits未定義,則此步驟將生成值0 )。
  2. 如果f < 0f > 20 ,則引發RangeError異常。
  3. x是這個數字值。
  4. 如果xNaN ,則返回字符串"NaN"
  5. 我們是空字符串。
  6. 如果x ≥ 0 ,則轉到步驟9。
  7. 讓我們成為"-"
  8. x = –x
  9. 如果x ≥ 10^21 ,設m = ToString(x)並轉到步驟20。
  10. 假設nn ÷ 10^f – x的精確數學值盡可能接近零的整數。 如果有兩個這樣的n ,挑選較大的n
  11. 如果n = 0 ,則讓m為字符串"0" 。 否則,設m為由n的十進製表示的數字組成的字符串(按順序,沒有前導零)。
  12. 如果f = 0 ,則轉到步驟20。
  13. km的字符數。
  14. 如果k > f ,則轉到步驟18。
  15. z是由字符'0'f+1–k出現組成的字符串。
  16. m是串zm的串聯。
  17. k = f + 1
  18. am的前k–f字符,設bm的剩餘f字符。
  19. m是三個字符串a"." ,和b
  20. 返回字符串sm的連接。

toFixed方法的length屬性是1

如果使用多個參數調用toFixed方法,則行為是未定義的(請參閱第15節)。

允許實現擴展對小於0或大於20fractionDigitstoFixed的行為。 在這種情況下toFixed不一定會為這些值拋出RangeError

注意 toFixed的輸出可能比toString更精確,因為toString只打印足夠的有效數字來區分相鄰數字值。 例如, (1000000000000000128).toString()返回"1000000000000000100" ,而(1000000000000000128).toFixed(0)返回"1000000000000000128"

Question

考慮下面的代碼:

for (var i=0;i<3;i++){
   var num = i + 0.50;
   var output = num + " " + Math.round(num) + " " + num.toFixed(0);
   alert(output);
}

在Opera 9.63中,我得到:

0.5 1 0

1.5 2 2

2.5 3 2

在FF 3.03中,我得到:

0.5 1 1

1.5 2 2

2.5 3 3

在IE 7中,我得到:

0.5 1 0

1.5 2 2

2.5 3 3

注意粗體結果。 為什麼存在這種不一致? 這是否意味著toFixed(0)應該被避免? 將數字四捨五入到最接近的整數的正確方法是什麼?




為了解決您的兩個原始問題/問題:

Math.round(num)vs num.toFixed(0)

這裡的問題在於它們應該總是給出相同結果的錯誤觀念。 事實上,它們受不同規則的約束。 看看負數,例如。 由於Math.round使用“半舍起”作為規則,即使Math.round(1.5)計算結果為2 ,您也會看到Math.round(-1.5)計算結果為-1

另一方面, Number.prototype.toFixed使用基本上相當於“離零的一半”的規則,根據規範的第6步 ,本質上說,將負數視為正數,然後加回最後的負面信號。 因此, (-1.5).toFixed(0) === "-2"(1.5).toFixed(0) === "2"是所有符合規範的瀏覽器中的真實語句。 (請注意,這些值是字符串,而不是數字。由於運算符的優先級, -1.5.toFixed(0)注意-1.5.toFixed(0)-(1.5).toFixed(0)=== -2Number )。

瀏覽器不一致

大多數現代瀏覽器 - 或者至少是本書編寫時應該支持的 瀏覽器(IE除外) - 都應該正確實現規格。 (根據Renee的評論 ,你在Opera中指出的toFixed問題已經被修正,大概是因為他們開始使用與Chrome相同的JS引擎。)還是值得重申的是,即使規範在所有瀏覽器中一致地實現,行為定義在規範中,特別是對於固定四捨五入,對於那些期望真正的數學準確性的“凡人”JS開發人員來說,仍然有點不直觀 - 參見Javascript toFixed Not Rounding , 這個“按照預期工作”引擎的例子。

結論

總之,這是兩種不同的函數,有兩種不同的返回類型和兩套不同的捨入規則。

正如其他人所建議的那樣,我也想說“使用適合您的特定用例的功能”(特別注意toFixed的特性,特別是IE的錯誤實現)。 我個人更傾向於推荐一些明確的Math.round/ceil/floor組合,正如其他人所說的那樣。 編輯: ...但是,回去後,閱讀你的說明,你的用例(四捨五入到一個整數)絕對要求適當命名的Math.round函數。




這絕對是這樣,如果你得到不一致的答案。

我只能猜測,你使用固定(0)的意圖是把一個十進制數轉成一個整數,在這一點上,我建議Math.floor()。 在這個問題上,最好的方法是討論一下。