问题
Consider the following code:
for (var i=0;i<3;i++){
var num = i + 0.50;
var output = num + " " + Math.round(num) + " " + num.toFixed(0);
alert(output);
}
In Opera 9.63 I get:
0.5 1 0
1.5 2 2
2.5 3 2
In FF 3.03 I get:
0.5 1 1
1.5 2 2
2.5 3 3
In IE 7 I get:
0.5 1 0
1.5 2 2
2.5 3 3
Note the bolded results. Why are this inconsistencies present? Does this mean that toFixed(0) should be avoided? What's the correct way to round a number to the nearest integer?
回答1:
Edit: To answer your edit, use Math.round. You could also prototype the Number object to have it do your bidding if you prefer that syntax.
Number.prototype.round = function() {
return Math.round(this);
}
var num = 3.5;
alert(num.round())
I've never used Number.toFixed() before (mostly because most JS libraries provide a toInt() method), but judging by your results I would say it would be more consistent to use the Math methods (round, floor, ceil) then toFixed if cross-browser consistency is what you are looking for.
回答2:
I think FF is doing the right thing with toFixed, since step 10 below says "If there are two such n, pick the larger n."
And as Grant Wagner said: Use Math.ceil(x) or Math.floor(x) instead of x.toFixed().
Everything below is from the ECMAScript Language Specification:
15.7.4.5
Number.prototype.toFixed (fractionDigits)Return a string containing the number represented in fixed-point notation with
fractionDigitsdigits after the decimal point. IffractionDigitsis undefined,0is assumed. Specifically, perform the following steps:
- Let
fbeToInteger(fractionDigits). (IffractionDigitsis undefined, this step produces the value0).- If
f < 0orf > 20, throw aRangeErrorexception.- Let
xbe this number value.- If
xisNaN, return the string"NaN".- Let
sbe the empty string.- If
x ≥ 0, go to step 9.- Let s be
"-".- Let
x = –x.- If
x ≥ 10^21, letm = ToString(x)and go to step 20.- Let
nbe an integer for which the exact mathematical value ofn ÷ 10^f – xis as close to zero as possible. If there are two suchn, pick the largern.- If
n = 0, letmbe the string"0". Otherwise, letmbe the string consisting of the digits of the decimal representation ofn(in order, with no leading zeroes).- If
f = 0, go to step 20.- Let
kbe the number of characters inm.- If
k > f, go to step 18.- Let
zbe the string consisting off+1–koccurrences of the character'0'.- Let
mbe the concatenation of stringszandm.- Let
k = f + 1.- Let
abe the firstk–fcharacters ofm, and letbbe the remainingfcharacters ofm.- Let
mbe the concatenation of the three stringsa,".", andb.- Return the concatenation of the strings
sandm.The
lengthproperty of thetoFixedmethod is1.If the
toFixedmethod is called with more than one argument, then the behaviour is undefined (see section 15).An implementation is permitted to extend the behaviour of
toFixedfor values offractionDigitsless than0or greater than20. In this casetoFixedwould not necessarily throwRangeErrorfor such values.NOTE The output of
toFixedmay be more precise thantoStringfor some values becausetoStringonly prints enough significant digits to distinguish the number from adjacent number values. For example,(1000000000000000128).toString()returns"1000000000000000100", while(1000000000000000128).toFixed(0)returns"1000000000000000128".
回答3:
To address your two original issues/questions:
Math.round(num) vs num.toFixed(0)
The issue here lies in the misconception that these should always give the same result. They are, in fact, governed by different rules. Look at negative numbers, for example. Because Math.round uses "round half up" as the rule, you will see that Math.round(-1.5) evaluates to -1 even though Math.round(1.5) evaluates to 2.
Number.prototype.toFixed, on the other hand, uses what is basically equivalent to "round half away from zero" as the rule, according to step 6 of the spec, which essentially says to treat negatives as positive numbers, and then add back the negative sign at the end. Thus, (-1.5).toFixed(0) === "-2" and (1.5).toFixed(0) === "2" are true statements in all spec-compliant browsers. Note that these values are strings, not numbers. Note further that both -1.5.toFixed(0) and -(1.5).toFixed(0) are === -2 (the number) due to operator precedence.
Browser inconsistencies
Most modern browsers—or at least the ones you might be expected to support at the time of this writing except for IE—should all implement the specs correctly. (According to Renee's comment, the toFixed issue you pointed out in Opera has been fixed, presumably since they started using the same JS engine as Chrome.) It's still worth reiterating that, even if the specs were implemented consistently across all browsers, the behavior defined in the spec, particularly for toFixed rounding, can still be a bit unintuitive for "mere mortal" JS developers who expect true mathematical accuracy—see Javascript toFixed Not Rounding and this "works as intended" bug that was filed on the V8 JS engine for examples.
Conclusion
In short, these are two different functions with two different return types and two different sets of rules for rounding.
As others have suggested, I would also like to say "use whichever function fits your particular use case" (taking special care to note the peculiarities of toFixed, especially IE's errant implementation). I would personally lean more towards recommending some explicit combination of Edit: ...though, after going back and reading your clarification, your use case (rounding to a whole number) definitely calls for the aptly-named Math.round/ceil/floor, again, as others have mentioned.Math.round function.
回答4:
toFixed() returns a string value. From Javascript: The Definitive Guide
Converts a number to a string that contains a specified number of digits after the decimal place.
Math.round() returns an integer.
Clearly, toFixed() seem to be more use for money, for example,
'$' + 12.34253.toFixed(2) = '$12.34'
It seems a big pity that toFixed() does not appear to round properly!
回答5:
Instead of toFixed(0) use Math.ceil() or Math.floor(), depending on what is required.
回答6:
It definitely seems that way, if you're getting inconsistent answers.
I can only guess that your intent with usin toFixed(0) is to turn a decimal number into an integer, at which point I recommend Math.floor(). There's a bit more discussion on the best way to do so in this question.
来源:https://stackoverflow.com/questions/566564/math-roundnum-vs-num-tofixed0-and-browser-inconsistencies