关于javascript:Math.round(num)与num.toFixed(0)和浏览器不一致

Math.round(num) vs num.toFixed(0) and browser inconsistencies

考虑以下代码:

1
2
3
4
5
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。如果您喜欢Number对象的语法,也可以对其进行原型制作。

1
2
3
4
5
Number.prototype.round = function() {
  return Math.round(this);
}
var num = 3.5;
alert(num.round())

我以前从未使用过Number.toFixed()(主要是因为大多数JS库提供了toInt()方法),但是从您的结果来看,我会说使用Math方法(roundceil),如果要跨浏览器保持一致性,则按toFixed


我认为FF用toFixed做正确的事情,因为下面的步骤10表示"如果有两个这样的n,则选择较大的n"。

正如Grant Wagner所说:使用Math.ceil(x)或Math.floor(x)代替x.toFixed()。

以下所有内容均来自ECMAScript语言规范:

15.7.4.5 Number.prototype.toFixed (fractionDigits)

Return a string containing the number represented in fixed-point
notation with fractionDigits digits after the decimal point. If
fractionDigits is undefined, 0 is assumed. Specifically, perform the
following steps:

  • Let f be ToInteger(fractionDigits). (If fractionDigits is undefined,
    this step produces the value 0).
  • If f < 0 or f > 20, throw a RangeError exception.
  • Let x be this number value.
  • If x is NaN, return the string "NaN".
  • Let s be the empty string.
  • If x ≥ 0, go to step 9.
  • Let s be "-".
  • Let x = –x.
  • If x ≥ 10^21, let m = ToString(x) and go to step 20.
  • Let n be an integer for which the exact mathematical value of
    n ÷ 10^f – x is as close to zero as possible. If there are two
    such n, pick the larger n.
  • If n = 0, let m be the string "0". Otherwise, let m be the
    string consisting of the digits of the decimal representation
    of n (in order, with no leading zeroes).
  • If f = 0, go to step 20.
  • Let k be the number of characters in m.
  • If k > f, go to step 18.
  • Let z be the string consisting of f+1–k occurrences of the
    character '0'.
  • Let m be the concatenation of strings z and m.
  • Let k = f + 1.
  • Let a be the first k–f characters of m, and let b be the
    remaining f characters of m.
  • Let m be the concatenation of the three strings a, ".", and b.
  • Return the concatenation of the strings s and m.
  • The length property of the toFixed method is 1.

    If the toFixed method is called with more than one argument, then the
    behaviour is undefined (see section 15).

    An implementation is permitted to extend the behaviour of toFixed for
    values of fractionDigits less than 0 or greater than 20. In this case
    toFixed would not necessarily throw RangeError for such values.

    NOTE The output of toFixed may be more precise than toString for
    some values because toString only prints enough significant digits
    to distinguish the number from adjacent number values. For example,
    (1000000000000000128).toString() returns "1000000000000000100", while
    (1000000000000000128).toFixed(0) returns "1000000000000000128".


    要解决您的两个原始问题/问题:

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

    这里的问题在于误解,认为它们应该总是产生相同的结果。实际上,它们受不同规则的约束。例如看负数。因为Math.round使用"四舍五入"作为规则,所以即使Math.round(1.5)评估为2,您也会看到Math.round(-1.5)评估为-1

    另一方面,根据规范第6步,Number.prototype.toFixed使用基本上等同于"从零开始舍入一半"的规则,该规则基本上说将负数视为正数,然后加负数在最后签名。因此,(-1.5).toFixed(0) ==="-2"(1.5).toFixed(0) ==="2"在所有符合规范的浏览器中均为true语句。请注意,这些值是字符串,而不是数字。还要注意,由于运算符的优先级,-1.5.toFixed(0)-(1.5).toFixed(0)均为=== -2(数字)。

    浏览器不一致

    除IE之外,大多数现代浏览器(或至少在撰写本文时可能期望您支持的浏览器)都应正确实现规范。 (根据Renee的评论,您在Opera中指出的toFixed问题已得到修复,大概是因为它们开始使用与Chrome相同的JS引擎。)即使这些规范在所有浏览器中都是一致实现的,仍然值得重申规范中定义的行为,特别是对于toFixed舍入,对于希望真正的数学准确性的"纯粹的" JS开发人员来说仍然有些不直观-请参阅Javascript toFixed Not Rounding和此"按预期工作"的错误以V8 JS引擎为例。

    结论

    简而言之,这是两个不同的函数,具有两个不同的返回类型和两个不同的舍入规则集。

    正如其他人所建议的,我也想说"使用适合您特定用例的任何功能"(要特别注意toFixed的特殊性,尤其是IE的错误实现)。 我个人会更倾向于推荐Math.round/ceil/floor的某些显式组合,就像其他人提到的那样。编辑:...尽管,在返回并阅读了说明后,您的用例(四舍五入为整数)绝对需要使用恰当命名的Math.round函数。


    toFixed()返回一个字符串值。来自Javascript:权威指南

    Converts a number to a string that contains a specified number of
    digits after the decimal place.

    Math.round()返回一个整数。

    显然,toFixed()似乎更多地用于金钱,例如,

    '$' + 12.34253.toFixed(2) = '$12.34'

    似乎很遗憾toFixed()似乎无法正确舍入!


    根据要求,使用Math.ceil()Math.floor()代替toFixed(0)


    如果您得到不一致的答案,肯定可以这样。

    我只能猜测,您对toFixed(0)的使用意图是将十进制数转换为整数,这时我建议使用Math.floor()。关于此问题的最佳方法还有更多讨论。