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);
}
В Опере 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 ответов
Редактировать: чтобы ответить на ваши изменения, используйте 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
если кросс-браузерная согласованность - это то, что вы ищете.
Чтобы решить ваши две оригинальные проблемы / вопросы:
Математическое окружение (num) против num.toFixed(0)
Проблема здесь заключается в неправильном представлении, что они всегда должны давать один и тот же результат. На самом деле они регулируются другими правилами. Посмотрите на отрицательные числа, например. Так как Math.round
как правило, использует "круглая половина вверх", вы увидите, что Math.round(-1.5)
оценивает-1
даже если Math.round(1.5)
оценивает 2
,
Number.prototype.toFixed
с другой стороны, как правило, используется то, что в основном эквивалентно "округлению половины до нуля", в соответствии с шагом 6 спецификации, в котором, по сути, говорится, что негативы должны рассматриваться как положительные числа, а затем добавляется отрицательный знак в конец. Таким образом, (-1.5).toFixed(0) === "-2"
а также(1.5).toFixed(0) === "2"
являются верными утверждениями во всех браузерах, соответствующих спецификации. Обратите внимание, что эти значения являются строками, а не числами. Обратите внимание, что оба -1.5.toFixed(0)
а также -(1.5).toFixed(0)
являются === -2
(число) из-за приоритета оператора.
Несоответствия браузера
Большинство современных браузеров -или, по крайней мере, те, которые вы, возможно, ожидаете поддерживать на момент написания этой статьи, за исключением IE- все должны правильно реализовывать спецификации. (Согласно комментарию Рене, toFixed
проблема, на которую вы указали в Opera, была исправлена, по-видимому, с тех пор, как они начали использовать тот же движок JS, что и Chrome.) Стоит еще раз повторить, что, даже если спецификации были реализованы последовательно во всех браузерах, поведение, определенное в спецификации, особенно для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
будь первымk–f
персонажиm
, и разрешиb
быть оставшимсяf
персонажиm
,- Позволять
m
быть объединением трех строкa
,"."
, а такжеb
,- Вернуть конкатенацию строк
s
а такжеm
,
length
собственностьtoFixed
метод1
,Если
toFixed
метод вызывается с более чем одним аргументом, тогда поведение не определено (см. раздел 15).Реализация может расширять поведение
toFixed
для значенийfractionDigits
меньше, чем0
или больше чем20
, В этом случаеtoFixed
не обязательно броситьRangeError
для таких значений.ПРИМЕЧАНИЕ. Выход
toFixed
может быть более точным, чемtoString
для некоторых значений, потому чтоtoString
печатает только достаточно значащие цифры, чтобы отличить число от соседних числовых значений. Например,(1000000000000000128).toString()
возвращается"1000000000000000100"
, в то время как(1000000000000000128).toFixed(0)
возвращается"1000000000000000128"
,
toFixed() возвращает строковое значение. Из Javascript: полное руководство
Преобразует число в строку, содержащую указанное количество цифр после десятичного знака.
Math.round () возвращает целое число.
Понятно, что toFixed(), кажется, больше используется для денег, например,
'$' + 12.34253.toFixed (2) = '$ 12,34'
Жаль, что toFixed() не округляется должным образом!
Вместо toFixed(0)
использование Math.ceil()
или же Math.floor()
в зависимости от того, что требуется.
Это определенно так кажется, если вы получаете противоречивые ответы.
Я могу только догадываться, что ваше намерение с помощью usin toFixed(0) - превратить десятичное число в целое число, после чего я рекомендую Math.floor(). В этом вопросе обсуждается лучший способ сделать это.