Math.round(num) vs 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);
}

В 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) следует избегать? Каков правильный способ округления числа до ближайшего целого?

Ответ 1

Изменить: Чтобы ответить на ваше редактирование, используйте 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, если согласованность между браузерами - это то, что вы ищете.

Ответ 2

Я думаю, что FF делает правильную вещь с toFixed, так как в шаге 10 ниже сказано: "Если есть два таких n, выберите большее n."

И как Грант Вагнер сказал: Используйте Math.ceil(x) или Math.floor(x) вместо x.toFixed().

Все ниже приведено Спецификация языка ECMAScript:

15.7.4.5 Number.prototype.toFixed (fractionDigits)

Возвращает строку, содержащую число, представленное в фиксированной точке обозначение с цифрой fractionDigits после десятичной точки. Если fractionDigits - undefined, 0. В частности, выполните следующие шаги:

  • Пусть f be ToInteger(fractionDigits). (Если fractionDigits undefined, на этом этапе создается значение 0).
  • Если f < 0 или f > 20, введите исключение RangeError.
  • Пусть x - это числовое значение.
  • Если x - NaN, верните строку "NaN".
  • Пусть s - пустая строка.
  • Если x ≥ 0, перейдите к шагу 9.
  • Пусть s "-".
  • Пусть x = –x.
  • Если x ≥ 10^21, m = ToString(x) и перейдите к шагу 20.
  • Пусть n - целое число, для которого точное математическое значение n ÷ 10^f – x как можно ближе к нулю. Если есть два такой n, выберите более крупный n.
  • Если n = 0, пусть m - строка "0". В противном случае пусть m - строка, состоящая из цифр десятичного представления of 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 вызывается с более чем одним аргументом, то поведение undefined (см. раздел 15).

Реализации разрешено расширять поведение toFixed для значения fractionDigits меньше, чем 0 или больше, чем 20. В этом случае toFixed не обязательно будет бросать RangeError для таких значений.

ПРИМЕЧАНИЕ Вывод toFixed может быть более точным, чем toString для некоторые значения, потому что toString печатает достаточно значительных цифр чтобы отличить число от смежных значений числа. Например, (1000000000000000128).toString() возвращает "1000000000000000100", тогда как (1000000000000000128).toFixed(0) возвращает "1000000000000000128".

Ответ 3

Для решения двух исходных вопросов/вопросов:

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

Проблема здесь заключается в заблуждении, что они всегда должны давать одинаковый результат. Фактически, они регулируются разными правилами. Посмотрите, например, на отрицательные числа. Поскольку Math.round использует "round half up" как правило, вы увидите, что Math.round(-1.5) оценивается как -1, хотя Math.round(1.5) оценивается как 2.

Number.prototype.toFixed, с другой стороны, использует то, что в принципе эквивалентно "round half away from zero" как правило, согласно на шаг 6 спецификации, который, по сути, говорит, чтобы рассматривать негативы как положительные числа, а затем добавить отрицательный знак в конце. Таким образом, (-1.5).toFixed(0) === "-2" и (1.5).toFixed(0) === "2" являются истинными операторами во всех spec-совместимых браузерах. Обратите внимание, что эти значения являются строками, а не цифрами. Обратите внимание, что как -1.5.toFixed(0), так и -(1.5).toFixed(0) являются === -2 (число) из-за приоритета оператора.

Несоответствия браузера

Большинство современных браузеров - или, по крайней мере, те, которые можно было бы ожидать на момент написания за исключением IE - все должны правильно реализовать спецификации. (Согласно комментарий Renee, проблема с toFixed, которую вы указали в Opera, была исправлена, по-видимому, с тех пор, как они начали использовать тот же движок JS, что и Chrome.) Он по-прежнему стоит повторив, что даже если спецификации были последовательно реализованы во всех браузерах, поведение, определенное в спецификации, особенно для округления toFixed, все еще может быть немного неинтуитивным для "простых смертных" разработчиков JS, которые ожидают истинной математической точности - см. Javascript toFixed Not Rounding и эта ошибка "работает как предполагалось", которая была отправлена ​​на V8 JS для примеров.

Заключение

Короче говоря, это две разные функции с двумя разными типами возврата и двумя разными наборами правил для округления.

Как и другие, я хотел бы также сказать, что "используйте ту, какая функция подходит вашему конкретному варианту использования" (с особой осторожностью обратите внимание на особенности toFixed, особенно IE errant). Я лично предпочел бы рекомендовать некоторую явную комбинацию Math.round/ceil/floor, опять же, как упомянули другие. Изменить:... хотя, вернувшись и прочитав ваше разъяснение, ваш прецедент (округление до целое число) определенно вызывает функцию aptly-named Math.round.

Ответ 4

toFixed() возвращает строковое значение. Из Javascript: окончательное руководство

Преобразует число в строку, содержащую указанное число цифр после десятичной точки.

Math.round() возвращает целое число.

Ясно, что toFixed(), похоже, больше полезен для денег, например,

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

Кажется очень жаль, что toFixed() не выглядит правильно округленным!

Ответ 5

Вместо toFixed(0) используйте Math.ceil() или Math.floor(), в зависимости от того, что требуется.

Ответ 6

Это определенно кажется таким, если вы получаете непоследовательные ответы.

Я могу только догадываться, что ваше намерение с usin toFixed (0) состоит в том, чтобы превратить десятичное число в целое число, после чего я рекомендую Math.floor(). Там немного больше обсуждений о том, как это сделать в this question.