Math.round (num) против num.toFixed (0) и несоответствия браузера - PullRequest
50 голосов
/ 19 февраля 2009

Рассмотрим следующий код:

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

Ответы [ 6 ]

32 голосов
/ 19 февраля 2009

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

12 голосов
/ 19 февраля 2009

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

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

Все, что ниже, взято из спецификации языка 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. Пусть s будет "-".
  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".

10 голосов
/ 04 декабря 2014

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

Математическое окружение (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.

6 голосов
/ 24 января 2012

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

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

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

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

'$' + 12.34253.toFixed (2) = '$ 12,34'

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

2 голосов
/ 19 февраля 2009

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

0 голосов
/ 19 февраля 2009

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

Я могу только догадываться, что ваше намерение с помощью usin toFixed (0) - превратить десятичное число в целое число, после чего я рекомендую Math.floor (). В этом вопросе .

немного больше обсуждается лучший способ сделать это.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...