繁体   English   中英

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

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

考虑以下代码:

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

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

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

这里的问题在于错误地认为这些应该总是给出相同的结果。 事实上,它们受不同的规则支配。 例如,看看负数。 因为Math.round使用“四舍五入”作为规则,您将看到Math.round(-1.5)计算结果为-1即使Math.round(1.5)计算结果为2

另一方面,根据规范的第 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函数。

我认为 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位。 如果fractionDigits未定义,则假定为0 具体执行以下步骤:

  1. fToInteger(fractionDigits) (如果fractionDigits未定义,则此步骤生成值0 )。
  2. 如果f < 0f > 20 ,则抛出RangeError异常。
  3. x为这个数值。
  4. 如果xNaN ,则返回字符串"NaN"
  5. s成为空字符串。
  6. 如果x ≥ 0 ,则转到步骤 9。
  7. 让我们成为"-"
  8. x = –x
  9. 如果x ≥ 10^21 ,让m = ToString(x)并转到步骤 20。
  10. n是一个整数,其中n ÷ 10^f – x的精确数学值尽可能接近于零。 如果有两个这样的n ,则选择较大的n
  11. 如果n = 0 ,则让m为字符串"0" 否则,让m是由n的十进制表示的数字组成的字符串(按顺序,没有前导零)。
  12. 如果f = 0 ,则转到步骤 20。
  13. km的字符数。
  14. 如果k > f ,则转到步骤 18。
  15. z是由f+1–k次出现的字符'0'组成的字符串。
  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或大于20fractionDigits值,允许实现扩展toFixed的行为。 在这种情况下toFixed不一定会为这些值抛出RangeError

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

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

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

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

显然, toFixed() 似乎更适合用于金钱,例如,

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

很遗憾toFixed()似乎没有正确舍入

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM