Почему parseInt (8,3) == NaN и parseInt (16,3) == 1?

191

Я читаю это, но меня смущает то, что написано в parseInt с главой аргумента radix

таблица результатов parseInt (_, 3)

Почему это parseInt(8, 3)NaNи parseInt(16, 3)1?

AFAIK 8 и 16 не являются номерами base-3, поэтому parseInt(16, 3)должны возвращаться NaNтоже

первые десять натуральных чисел базы-3

Дэвид Фаринелли
источник
4
Еще одна проблема, которая была бы решена с помощью статической типизации (или, по крайней мере, неявного преобразования целых чисел в строки): P
Navin
4
@Navin Это не имеет ничего общего со статической и динамической типизацией (как вы сами заметили). Проблема здесь слабая, в отличие от строгой типизации.
Свен Марнах
12
Когда я увидел заголовок этого вопроса, я подумал про себя: «Это, вероятно, потому что loljavascript». Видя ответы, я считаю, что мой инстинкт был в основном правильным.
Бен Милвуд

Ответы:

373

Это то, о чем люди спотыкаются все время, даже когда знают об этом. :-) Вы видите это по той же причине parseInt("1abc")возвращает 1: parseIntостанавливается на первом недопустимом символе и возвращает все, что у него есть в этой точке. Если нет подходящих символов для анализа, он возвращается NaN.

parseInt(8, 3)означает «анализ "8"в базе 3» (обратите внимание, что он преобразует число 8в строку; подробности в спецификации ). Но в базе 3, одноразрядные числа просто 0, 1и 2. Это все равно что просить его разобрать "9"в восьмеричном виде. Поскольку не было ни одного действительных символов, вы получили NaN.

parseInt(16, 3)просит его проанализировать "16"в базе 3. Так как он может анализировать 1, он делает, а затем останавливается на 6потому, что он не может проанализировать его. Так и возвращается 1.


Поскольку этому вопросу уделяется много внимания и он может быть высоко оценен в результатах поиска, вот краткое изложение вариантов преобразования строк в числа в JavaScript с их различными особенностями и приложениями (взято из другого моего ответа здесь, на SO):

  • parseInt(str[, radix])- Преобразует как можно большую часть начала строки в целое (целое) число, игнорируя дополнительные символы в конце. Так и parseInt("10x")есть 10; xигнорируется. Поддерживает необязательный аргумент radix (number base), parseInt("15", 16)как и 21( 15в шестнадцатеричном виде) Если основание отсутствует, предполагается десятичное число, если строка не начинается с 0x(или 0X), и в этом случае она пропускает их и принимает шестнадцатеричное значение. (Некоторые браузеры использовали строки, начинающиеся с 0восьмеричного; такое поведение никогда не указывалось, и было специально запрещено в спецификации ES5.) Возвращает, NaNесли не найдено разборчивых цифр.

  • parseFloat(str)- Нравится parseInt, но делает числа с плавающей запятой и поддерживает только десятичные. Снова дополнительные символы в строке игнорируются, parseFloat("10.5x")как и 10.5( xигнорируется). Как только десятичный поддерживаются, parseFloat("0x15")это 0(поскольку анализ заканчивается на x). Возвращает, NaNесли не найдено разборчивых цифр.

  • Унарный +, например +str- (например, неявное преобразование) Преобразует всю строку в число, используя числа с плавающей запятой и стандартную нотацию JavaScript (только цифры и десятичная точка = десятичная; 0xпрефикс = шестнадцатеричный; 0oпрефикс = восьмеричный [ES2015 +]; некоторые реализации расширяют его рассматривать ведущий 0как восьмеричное, но не в строгом режиме). +"10x"это NaNпотому , что xэто не игнорируется. +"10"это 10, +"10.5"это 10.5, +"0x15"это 21, +"0o10"это 8[ES2015 +]. Имеет Гоча: +""это 0не NaNкак вы могли бы ожидать.

  • Number(str)- Точно так же, как неявное преобразование (например, как унарный +выше), но медленнее в некоторых реализациях. (Не то чтобы это могло иметь значение.)

TJ Crowder
источник
8
Итак, parseIntпервое использование toStringпо первому аргументу? Это имело бы смысл.
evolutionxbox
16
@evolutionxbox: Да, это первый шаг parseIntалгоритма: ecma-international.org/ecma-262/7.0/…
TJ Crowder
5
Я полагаю, что 123e-2дает, 1так как он 1.23сначала превращается в , а затем синтаксический анализ останавливается на десятичной запятой?
ilkkachu
6
«Это то, о чем люди спотыкаются все время, даже когда они знают об этом» -> я единственный, кто считает, что это должно быть ошибкой? То же самое в Java, например, даст вам NumberFormatExceptionкаждый раз.
Вим Deblauwe
4
@SvenMarnach: эта часть parseInt(приведение первого аргумента к строке) имеет смысл. Цель parseIntсостоит в том, чтобы разобрать строку в целое число. Так что, если вы даете ему что-то, что не является строкой, имеет смысл начинать с строкового представления. То, что он делает после этого, - целая «другая история»
TJ Crowder
54

По той же причине, что

>> parseInt('1foobar',3)
<- 1

В ДоПе , parseIntпринимает строку. И

Если строка не является строкой, то она преобразуется в строку

Итак 16, 8или '1foobar'сначала преобразуется в строку.

затем

Если parseIntвстречает символ, который не является цифрой в указанном основании, он игнорирует его и все последующие символы

Это означает, что он преобразуется туда, где может. Символы 6, 8и foobarигнорируются, и преобразуется только то, что перед ними. Если ничего нет, NaNвозвращается.

njzk2
источник
0
/***** Radix 3: Allowed numbers are [0,1,2] ********/
parseInt(4, 3); // NaN - We can't represent 4 using radix 3 [allowed - 0,1,2]

parseInt(3, 3); // NaN - We can't represent 3 using radix 3 [allowed - 0,1,2]

parseInt(2, 3); // 2   - yes we can !

parseInt(8, 3); // NaN - We can't represent 8 using radix 3 [allowed - 0,1,2]

parseInt(16, 3); // 1  
//'16' => '1' (6 ignored because it not in [0,1,2])    

/***** Radix 16: Allowed numbers/characters are [0-9,A-F] *****/ 
parseInt('FOX9', 16); // 15  
//'FOX9' => 'F' => 15 (decimal value of 'F')
// all characters from 'O' to end will be ignored once it encounters the out of range'O'
// 'O' it is NOT in [0-9,A-F]

Еще несколько примеров:

parseInt('45', 13); // 57
// both 4 and 5 are allowed in Radix is 13 [0-9,A-C]

parseInt('1011', 2); // 11 (decimal NOT binary)

parseInt(7,8); // 7
// '7' => 7 in radix 8 [0 - 7]

parseInt(786,8); // 7 
// '78' => '7' => 7 (8 & next any numbers are ignored bcos 8 is NOT in [0-7])

parseInt(76,8); // 62 
// Both 7 & 6 are allowed '76' base 8 decimal conversion is 62 base 10 
SridharKritha
источник