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

时间:2009-02-19 18:31:57

标签: javascript cross-browser

请考虑以下代码:

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)?将数字舍入到最接近的整数的正确方法是什么?

6 个答案:

答案 0 :(得分:32)

修改:要回答您的修改,请使用Math.round。如果您更喜欢这种语法,也可以对Number对象进行原型设计,让它进行出价。

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

我之前从未使用Number.toFixed()(主要是因为大多数JS库提供了toInt()方法),但根据您的结果判断,我会说使用{{1}会更加一致方法(Mathroundfloor)然后ceil如果您正在寻找跨浏览器一致性。

答案 1 :(得分:11)

我认为FF正在使用toFixed做正确的事情,因为下面的第10步说“如果有两个这样的n,请选择更大的n。”

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

以下所有内容均来自ECMAScript Language Specification

  

15.7.4.5 Number.prototype.toFixed (fractionDigits)

     

返回包含定点数表示的数字的字符串   小数点后面带有fractionDigits数字的表示法。如果   fractionDigits未定义,假设为0。具体来说,执行   以下步骤:

     
      
  1. fToInteger(fractionDigits)。 (如果fractionDigits未定义,   此步骤生成值0)。
  2.   
  3. 如果f < 0f > 20,则抛出RangeError例外。
  4.   
  5. x为此数字值。
  6.   
  7. 如果xNaN,请返回字符串"NaN"
  8.   
  9. s成为空字符串。
  10.   
  11. 如果x ≥ 0,请转到步骤9。
  12.   
  13. 我们是"-"
  14.   
  15. 允许x = –x
  16.   
  17. 如果x ≥ 10^21,请m = ToString(x)并转到第20步。
  18.   
  19. n为整数,其精确数学值为   n ÷ 10^f – x尽可能接近零。如果有两个   这样的n,选择较大的n
  20.   
  21. 如果n = 0,请m为字符串"0"。否则,让m成为。{1}}   由十进制表示的数字组成的字符串   n的顺序(按顺序,没有前导零)。
  22.   
  23. 如果f = 0,请转到步骤20.
  24.   
  25. km中的字符数。
  26.   
  27. 如果k > f,请转到步骤18.
  28.   
  29. z成为由f+1–k次出现的字符串组成的字符串   字符'0'
  30.   
  31. m成为字符串zm的串联。
  32.   
  33. 允许k = f + 1
  34.   
  35. a成为k–f的第一个m字符,让b成为   f的剩余m个字符。
  36.   
  37. m成为三个字符串a"."b的串联。
  38.   
  39. 返回字符串sm的串联。
  40.         

    length方法的toFixed属性为1

         

    如果使用多个参数调用toFixed方法,那么   行为未定义(见第15节)。

         

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

         

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

答案 2 :(得分:10)

解决您的两个原始问题:

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

这里的问题在于误解这些应该总是给出相同的结果。事实上,它们受不同规则的约束。例如,查看负数。由于Math.round使用"round half up"作为规则,因此即使Math.round(-1.5)评估为-1,您也会看到Math.round(1.5)评估为2

另一方面,

Number.prototype.toFixed根据"round half away from zero"使用基本等同于step 6 of the spec的规则,基本上将负数视为正数,然后添加在最后支持负号。因此,(-1.5).toFixed(0) === "-2"(1.5).toFixed(0) === "2"在所有符合规范的浏览器中都是真实的语句。请注意,这些值是字符串,而不是数字。请注意,由于运算符优先级,-1.5.toFixed(0)-(1.5).toFixed(0)都是=== -2(数字)。

浏览器不一致

大多数现代浏览器 - 或者至少是您在撰写本文时可能会支持的浏览器 except for IE - 都应该正确实现规范。 (根据Renee's comment,您在Opera中指出的toFixed问题已得到修复,大概是因为他们开始使用与Chrome相同的JS引擎。)即使它仍然值得重申,即使规范在所有浏览器中都是一致的,规范中定义的行为,尤其是toFixed四舍五入的行为,对于“凡人”而言仍然有点不直观。期望真正的数学准确性的JS开发人员 - 请参阅V8 JS引擎上提交的Javascript toFixed Not Roundingthis "works as intended" bug作为示例。

结论

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

正如其他人所建议的那样,我还想说&#34;使用适合您特定用例的任何功能&#34; (特别注意注意toFixed的特殊性,尤其是IE的错误实施)。 我个人倾向于更多地推荐Math.round/ceil/floor的明确组合,正如其他人所提到的那样。 编辑: ......但是,在回去阅读之后你的澄清,你的用例(四舍五入到整数)绝对需要恰当命名的Math.round函数。

答案 3 :(得分:6)

toFixed()返回一个字符串值。来自Javascript:The Definitive Guide

  

将数字转换为包含指定数量的字符串   小数点后的数字。

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

显然,toFixed()似乎更有用,例如,

  

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

似乎很可惜 toFixed()似乎没有正确舍入!

答案 4 :(得分:2)

而不是toFixed(0)使用Math.ceil()Math.floor(),取决于所需内容。

答案 5 :(得分:0)

如果你的答案不一致,肯定会这样。

我只能猜测你使用toFixed(0)的意图是将十进制数转换为整数,此时我推荐使用Math.floor()。在this question中有关于最佳方法的更多讨论。