parseInt() неправильно анализирует числовые литералы с показателем степени

Я только что заметил, что функция parseInt не заботится о десятичных дробях в случае целых чисел (чисел, содержащих символ e).

Возьмем пример: -3.67394039744206e-15

> parseInt(-3.67394039744206e-15)
-3
> -3.67394039744206e-15.toFixed(19)
-3.6739e-15
> -3.67394039744206e-15.toFixed(2)
-0
> Math.round(-3.67394039744206e-15)
0

Я ожидал, что parseInt также вернет 0. Что происходит на нижнем уровне? Почему parseInt возвращает 3 в этом случае (некоторые фрагменты исходного кода будут оценены)?

В этом примере я использую node v0.12.1, но я ожидаю, что то же самое произойдет в браузере и других движках JavaScript.


person Ionică Bizău    schedule 11.05.2015    source источник
comment
Мне также интересно, что -3.67394039744206e-15.toFixed(19) делает? (-3.67394039744206e-15).toFixed(19) возвращает другой результат.   -  person mpen    schedule 11.05.2015
comment
@Mark Могу я задать новый вопрос об этом? :) Я тоже это заметил.   -  person Ionică Bizău    schedule 11.05.2015
comment
@Mark В скобках возвращается то же самое, но в виде строки.   -  person royhowie    schedule 11.05.2015
comment
ecma-international.org/ecma-262/5.1/# сек-15.1.2.2   -  person Yellen    schedule 11.05.2015
comment
@IonicăBizău Я бы так сказал. Это это другой вопрос. Он работает, как и ожидалось, если убрать отрицательный знак, но не с ним.   -  person mpen    schedule 11.05.2015
comment
Я понимаю. -3.67394039744206e-15.toFixed(19) совпадает с -((3.67394039744206e-15).toFixed(19)), а - (как и +) неявно преобразует его в число (это как сокращение для Number(), например, +'14' равно 14, а -'14' равно -14).   -  person Sebastian Simon    schedule 11.05.2015
comment
@Xufox Вау ... Я даже не знаю, что об этом сказать. Я понимаю, как +/- неявно преобразует, но этот отрицательный знак действительно должен был быть проанализирован как часть числа IMO.   -  person mpen    schedule 11.05.2015
comment
@Mark, да, но порядок операторов имеет другое мнение, и вам лучше доверять принятым тогда решениям по этому поводу, потому что в противном случае это скорее всего испортит другие вещи или усложнит ситуацию намного. =P Представьте себе мир, в котором obj.a-obj.b приведет к ReferenceError: a is not defined или что-то в этом роде…   -  person Sebastian Simon    schedule 11.05.2015
comment
Разве не parseInt(-3.67394039744206e-15) === -3?   -  person Salman A    schedule 11.05.2015
comment
Node v0.12.2 возвращает -3 и -0 вместо ваших 3 и 0.   -  person Cees Timmerman    schedule 11.05.2015
comment
@CeesTimmerman То же самое, но пример был неправильным. :)   -  person Ionică Bizău    schedule 11.05.2015


Ответы (6)


Я думаю, причина в том, что parseInt преобразует переданное значение в строку, вызвав ToString, который вернет "-3.67394039744206e-15", затем проанализирует это так он посчитает -3 и вернет его.

документация mdn

Функция parseInt преобразует свой первый аргумент в строку, анализирует ее и возвращает целое число или NaN.

person Arun P Johny    schedule 11.05.2015
comment
Похоже на это - по крайней мере, из документов Mozilla: если parseInt встречает символ, который не является числом в указанной системе счисления, он игнорирует его и все последующие символы и возвращает целочисленное значение, проанализированное до этой точки. parseInt усекает числа до целочисленных значений. Допускаются начальные и конечные пробелы. - person Evan Knowles; 11.05.2015
comment
Таким образом, показатель степени в основном полностью игнорируется? - person Sebastian Simon; 11.05.2015
comment
Да... это так, что parseInt("200USD") вернет 200, а не сбой или бросок. Удобная функция, если вы понимаете, как она работает. - person Alex McMillan; 11.05.2015
comment
@AlexMcMillan да .... Так что я думаю, что причиной такого поведения является преобразование числа в строку - person Arun P Johny; 11.05.2015
comment
@ArunPJohny Спасибо! И еще один момент: ссылка на исходный код, где все это происходит. Большое спасибо! - person Ionică Bizău; 11.05.2015

parseInt(-3.67394039744206e-15) === -3

Функция parseInt ожидает строку в качестве первого аргумента. . JavaScript будет вызывать метод toString за сценой, если аргумент не является строкой. Таким образом, выражение оценивается следующим образом:

(-3.67394039744206e-15).toString()
// "-3.67394039744206e-15"
parseInt("-3.67394039744206e-15")
// -3

-3.67394039744206e-15.toFixed(19) === -3.6739e-15

Это выражение анализируется как:

  • Унарный - оператор
  • Числовой литерал 3.67394039744206e-15
  • .toFixed() -- метод доступа к свойству, имя свойства и вызов функции

Способ анализа числовых литералов описан здесь. Интересно, что +/- не являются частью числового литерала. Итак, у нас есть:

// property accessor has higher precedence than unary - operator
3.67394039744206e-15.toFixed(19)
// "0.0000000000000036739"
-"0.0000000000000036739"
// -3.6739e-15

Аналогично для -3.67394039744206e-15.toFixed(2):

3.67394039744206e-15.toFixed(2)
// "0.00"
-"0.00"
// -0
person Salman A    schedule 11.05.2015

Если проанализированная строка (лишенная знака +/-) содержит какой-либо символ, который не является цифрой системы счисления (10 в вашем случае), то создается подстрока, содержащая все остальные символы до такого символа, отбрасывая эти нераспознанные символы.

В случае -3.67394039744206e-15 начинается преобразование, и система счисления определяется как основание 10 -> Преобразование происходит до тех пор, пока не встретится '.' который не является допустимым символом в базе 10. Таким образом, фактически преобразование происходит для 3, которое дает значение 3, а затем применяется знак, таким образом -3.

Для логики реализации — http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.2

Другие примеры -

alert(parseInt("2711e2", 16));
alert(parseInt("2711e2", 10));

Отметить:

Основание начинается с основания 10.

Если первым символом является "0", он переключается на основание 8.

Если следующим символом является "x", он переключается на основание 16.

person Yellen    schedule 11.05.2015
comment
Итак, подождите, e не считается показателем степени, потому что может быть шестнадцатеричным числом 14? - person Sebastian Simon; 11.05.2015
comment
Да, точно. Вот как он разделяет строку на основе системы счисления. И по умолчанию основание равно 10. - person Yellen; 11.05.2015
comment
@Seram: На самом деле, по умолчанию система счисления определяется автоматически на основе обозначений javascript для целых чисел. Например, "0x12" определяется как шестнадцатеричный, тогда как "012" определяется как восьмеричный, а "12" определяется как десятичный. - person slebetman; 11.05.2015
comment
@Seram: Хм .. похоже, parseInt() больше не определяет начальные нули как восьмеричные. Раньше так делал - person slebetman; 11.05.2015
comment
@Seram, правда - это обнаруживается на основе первых встреченных символов. Моя ошибка там. :) - person Yellen; 11.05.2015
comment
@Xufox: я упомянул об этом. Кроме того, последняя спецификация по-прежнему ES5. ES6 все еще находится в нескольких месяцах от завершения - person slebetman; 11.05.2015
comment
Предполагается, что это 10, если вы передаете десятичное число - person Yellen; 11.05.2015
comment
Он останавливается на '.' примечание'. - person Taemyr; 11.05.2015

Он пытается преобразовать строки в целые числа. Я подозреваю, что ваши поплавки сначала преобразуются в строки. Затем, вместо того, чтобы анализировать все значение, а затем округлять, он использует функцию синтаксического анализа символ за символом и останавливается, когда доходит до первой десятичной точки, игнорируя любые десятичные разряды или показатели степени.

Несколько примеров здесь http://www.w3schools.com/jsref/jsref_parseint.asp

person Phil    schedule 11.05.2015
comment
Я точно знаю. Но на этот раз у него действительно было несколько полезных примеров. Ха-ха - person Phil; 11.05.2015

parseInt предназначен для разбора строки, а не числа:

Функция parseInt() анализирует строковый аргумент и возвращает целое число по указанной системе счисления (основание в математических системах счисления).

И parseInt вызывает функцию ToString, в которой все нечисловые символы игнорируются.

Вы можете использовать Math.round, который также анализирует строки и округляет число до ближайшего целого:

Math.round("12.2e-2") === 0 //true
person João Pimentel Ferreira    schedule 04.02.2019

Math.round("12.2e-2") может округляться вверх или вниз в зависимости от значения. Следовательно, может вызвать проблемы.

new Number("3.2343e-10").toFixed(0) может решить проблему.

person user10090004    schedule 25.09.2019