Чому саме parseInt (8,3) == NaN та parseInt (16,3) == 1?


191

Я читаю це, але мене бентежить те, що написано в синтаксичному аналізі з розділом аргументу радіації

таблиця результатів аналізу (_, 3)

Чому parseInt(8, 3)NaNі parseInt(16, 3)1?

AFAIK 8 і 16 не є базовими-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ігнорується. Підтримує необов'язковий аргумент (база бази чисел), так 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 +]. Має gotcha: +""є 0, не так, NaNяк ви могли очікувати.

  • Number(str)- Так само, як неявна конверсія (наприклад, як унарне +вище), але повільніше в деяких реалізаціях. (Не те, що це, мабуть, має значення.)


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кожен раз.
Вім Деблауве

4
@SvenMarnach: Ця частина parseInt(примушування першого аргументу до рядку) має сенс. Мета parseInt- проаналізувати рядок на ціле число. Отже, якщо ви надаєте йому щось, що не є рядком, то, щоб почати строкове представлення цього тексту, має сенс Що після цього - це ціла «північна історія ...
TJ Crowder

54

З тієї ж причини, що

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

У допі , parseIntприймає рядок. І

Якщо рядок не є рядком, він перетворюється на рядок

Отже 16, 8або '1foobar'спочатку перетворюється на рядок.

Тоді

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

Це означає, що він перетворює туди, куди може. І 6, 8і foobarігноруються, і конвертується лише те, що є раніше. Якщо нічого немає, NaNповертається.


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 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.