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

考虑下面的代码:

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对象原型化,使其可以进行出价。

 Number.prototype.round = function() { return Math.round(this); } var num = 3.5; alert(num.round()) 

我从来没有使用Number.toFixed()之前(主要是因为大多数JS库提供了一个toInt()方法),但根据您的结果来看,我会说使用Math方法( roundfloorceil )会更一致。然后toFixed如果跨浏览器一致性是你在找什么。

我认为FF与toFixed是正确的,因为下面的第10步说:“如果有两个这样的n,select较大的n”。

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

以下全部来自ECMAScript语言规范 :

15.7.4.5 Number.prototype.toFixed (fractionDigits)

返回一个string,其中包含以小数点后的小数点位数表示的定点符号表示的数字。 如果fractionDigits未定义,则假定为0 。 具体来说,执行以下步骤:

  1. fToInteger(fractionDigits) 。 (如果fractionDigits未定义,则此步骤将生成值0 )。
  2. 如果f < 0f > 20 ,则引发RangeErrorexception。
  3. x是这个数字值。
  4. 如果xNaN ,则返回string"NaN"
  5. 我们是空string。
  6. 如果x ≥ 0 ,则转到步骤9。
  7. 让我们成为"-"
  8. x = –x
  9. 如果x ≥ 10^21 ,设m = ToString(x)并转到步骤20。
  10. 假设nn ÷ 10^f – x的精确math值尽可能接近零的整数。 如果有两个这样的n ,挑选较大的n
  11. 如果n = 0 ,则让m为string"0" 。 否则,设m为由n的十进制表示的数字组成的string(按顺序,没有前导零)。
  12. 如果f = 0 ,则转到步骤20。
  13. km的字符数。
  14. 如果k > f ,则转到步骤18。
  15. z是由字符'0'f+1–k出现组成的string。
  16. m是串zm的串联。
  17. k = f + 1
  18. am的前k–f字符,设bm的剩余f字符。
  19. m是三个stringa"." ,和b
  20. 返回stringsm的连接。

toFixed方法的length属性是1

如果使用多个参数调用toFixed方法,则行为是未定义的(请参阅第15节)。

允许实现扩展对小于0或大于20fractionDigitstoFixed的行为。 在这种情况下toFixed不一定会为这些值抛出RangeError

注意 toFixed的输出可能比toString更精确,因为toString只打印足够的有效数字来区分相邻数字值。 例如, (1000000000000000128).toString()返回"1000000000000000100" ,而(1000000000000000128).toFixed(0)返回"1000000000000000128"

toFixed()返回一个string值。 从Javascript:权威指南

将数字转换为包含小数点后指定位数的string。

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

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

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

toFixed()看起来好像很可惜!

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

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"是所有符合规范的浏览器中的真实语句。 (请注意,这些值是string,而不是数字。由于运算符的优先级, -1.5.toFixed(0)注意-1.5.toFixed(0)-(1.5).toFixed(0)=== -2Number )。

浏览器不一致

大多数现代浏览器 – 或者至less是本书编写时应该支持的 浏览器(IE除外) – 都应该正确实现规格。 (根据Renee的评论 ,你在Opera中指出的toFixed问题已经被修正,大概是因为他们开始使用与Chrome相同的JS引擎。)仍然值得重申的是,即使规范在所有浏览器中一致地实现,行为定义在规范中,特别是对于固定四舍五入,对于那些期望真正的math准确性的“凡人”JS开发人员来说,仍然有点不直观 – 请参阅JavaScript toFixed Not Rounding和这个“按预期工作”的错误 ,引擎的例子。

结论

总之,这是两种不同的函数,有两种不同的返回types和两套不同的舍入规则。

正如其他人所build议的那样,我也想说“使用适合您的特定用例的function”(特别注意toFixed的特性,特别是IE的错误实现)。 我个人更倾向于推荐一些明确的Math.round/ceil/floor组合,正如其他人所说的那样。 编辑: …但是,回去后,阅读你的说明,你的用例(四舍五入到一个整数)绝对要求适当命名的Math.round函数。

而不是toFixed(0)使用Math.ceil()Math.floor() ,取决于需要什么。

这绝对是这样,如果你得到不一致的答案。

我只能猜测,你使用固定(0)的意图是把一个十进制数转成一个整数,在这一点上,我build议Math.floor()。 在这个问题上,最好的方法是讨论一下。