Математика.раунд (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);
}

в 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 ответов


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


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

и Грант Вагнер сказал:математика.ceil (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 is 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".


чтобы обратиться к вашим двум оригинал вопросы:

математика.раунд (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, что и Хром.) По-прежнему стоит повторить, что, даже если спецификации были реализованы последовательно во всех браузерах, поведение, определенное в спецификации, особенно для toFixed округление, все еще может быть немного неинтуитивным для" простых смертных " разработчиков JS, которые ожидают истинной математической точности-см. Javascript toFixed не округление и это" работает по назначению " ошибка это было подано на двигатель V8 JS для примеров.

вывод

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

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


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

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

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

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

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

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


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


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

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