[英]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
方法( round
、 floor
、 ceil
)会更一致如果跨浏览器一致性是您正在寻找的,则toFixed
。
要解决您的两个原始问题/问题:
这里的问题在于错误地认为这些应该总是给出相同的结果。 事实上,它们受不同的规则支配。 例如,看看负数。 因为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
。 具体执行以下步骤:
- 令
f
为ToInteger(fractionDigits)
。 (如果fractionDigits
未定义,则此步骤生成值0
)。- 如果
f < 0
或f > 20
,则抛出RangeError
异常。- 设
x
为这个数值。- 如果
x
是NaN
,则返回字符串"NaN"
。- 让
s
成为空字符串。- 如果
x ≥ 0
,则转到步骤 9。- 让我们成为
"-"
。- 让
x = –x
。- 如果
x ≥ 10^21
,让m = ToString(x)
并转到步骤 20。- 设
n
是一个整数,其中n ÷ 10^f – x
的精确数学值尽可能接近于零。 如果有两个这样的n
,则选择较大的n
。- 如果
n = 0
,则让m
为字符串"0"
。 否则,让m
是由n
的十进制表示的数字组成的字符串(按顺序,没有前导零)。- 如果
f = 0
,则转到步骤 20。- 令
k
为m
的字符数。- 如果
k > f
,则转到步骤 18。- 设
z
是由f+1–k
次出现的字符'0'
组成的字符串。- 让
m
是字符串z
和m
的串联。- 让
k = f + 1
。- 设
a
为m
的前k–f
字符,设b
为m
的其余f
字符。- 让
m
是三个字符串a
,"."
的串联"."
,和b
。- 返回字符串
s
和m
的串联。
toFixed
方法的length
属性是1
。如果使用多个参数调用
toFixed
方法,则行为未定义(参见第 15 节)。对于小于
0
或大于20
的fractionDigits
值,允许实现扩展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.