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 предполагается. В частности, выполните следующие действия:

  1. Позволять f быть ToInteger(fractionDigits), (Если fractionDigits не определено, этот шаг производит значение 0).
  2. Если f < 0 или же f > 20Кидай RangeError исключение.
  3. Позволять x будь это числовое значение.
  4. Если x является NaNвернуть строку "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. Позволять k быть количество символов в m,
  14. Если k > fперейдите к шагу 18.
  15. Позволять z быть строкой, состоящей из f+1–k появления персонажа '0',
  16. Позволять m быть объединением строк z а также m,
  17. Позволять k = f + 1,
  18. Позволять a будь первым k–f персонажи m, и разреши b быть оставшимся f персонажи m,
  19. Позволять m быть объединением трех строк a, ".", а также b,
  20. Вернуть конкатенацию строк 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(). В этом вопросе обсуждается лучший способ сделать это.

Другие вопросы по тегам