Dlaczego jest tak, że parsowanie (8,3) == NaN i parsowanie (16,3) == 1?

191

Czytam to, ale mylę to, co napisano w parseInt z rozdziałem o argumentach radix

tabela wyników analizy (_, 3)

Dlaczego to parseInt(8, 3)NaNi parseInt(16, 3)1?

AFAIK 8 i 16 nie są bazowe-3 cyfry, to parseInt(16, 3)należy zwrócić NaNzbyt

pierwsze dziesięć liczb naturalnych w bazie 3

Devid Farinelli
źródło
4
Jeszcze inny problem, który zostałby rozwiązany przez wpisywanie statyczne (lub przynajmniej niejawne przekształcanie liczb całkowitych w ciągi znaków): P
Navin
4
@Navin Nie ma to nic wspólnego z pisaniem statycznym a dynamicznym (jak sam się zauważa). Problem tutaj jest słaby w przeciwieństwie do silnego pisania.
Sven Marnach,
12
Kiedy zobaczyłem tytuł tego pytania, pomyślałem: „prawdopodobnie dlatego, że loljavascript”. Widząc odpowiedzi, uważam, że mój instynkt był zasadniczo poprawny.
Ben Millwood

Odpowiedzi:

373

Ludzie ciągle się potykają, nawet jeśli wiedzą o tym. :-) Widzisz to z tego samego powodu parseInt("1abc")zwraca 1: parseIntzatrzymuje się na pierwszym nieprawidłowym znaku i zwraca cokolwiek ma w tym momencie. Jeśli nie ma poprawnych znaków do przeanalizowania, zwraca NaN.

parseInt(8, 3)oznacza „parsuj "8"w bazie 3” (zwróć uwagę, że konwertuje liczbę 8na ciąg; szczegóły w specyfikacji ). Ale podstawy 3, numery jednocyfrowe to tylko 0, 1i 2. To tak, jakby poprosić go o parsowanie "9"ósemkowe. Ponieważ nie było żadnych ważnych znaków, masz NaN.

parseInt(16, 3)prosi go o parsowanie "16"w bazie 3. Ponieważ może on parsować 1, robi, a następnie zatrzymuje się na, 6ponieważ nie może go parsować. Więc wraca 1.


Ponieważ pytanie to zyskuje wiele uwagi i może zajmować wysokie pozycje w wynikach wyszukiwania, oto przegląd opcji konwersji ciągów znaków na liczby w JavaScript, z ich różnymi osobliwościami i aplikacjami (przeniesionymi z innej mojej odpowiedzi tutaj na SO):

  • parseInt(str[, radix])- Konwertuje jak najwięcej początku ciągu na liczbę całkowitą (całkowitą), ignorując dodatkowe znaki na końcu. Tak parseInt("10x")jest 10; xjest ignorowany. Obsługuje opcjonalny argument podstawa (podstawa liczbowa), więc parseInt("15", 16)jest 21( 15w systemie szesnastkowym). Jeśli nie ma podstawki, przyjmuje liczbę dziesiętną, chyba że ciąg zaczyna się od 0x(lub 0X), w którym to przypadku pomija je i przyjmuje wartość szesnastkową. (Niektóre przeglądarki traktowały ciągi zaczynające się 0na ósemkowe; takie zachowanie nigdy nie zostało określone i zostało wyraźnie zabronione w specyfikacji ES5.) Zwraca, NaNjeśli nie można znaleźć analizowalnych cyfr.

  • parseFloat(str)- Podobnie jak parseInt, ale robi liczby zmiennoprzecinkowe i obsługuje tylko dziesiętne. Ponownie dodatkowe znaki na łańcuchu są ignorowane, więc parseFloat("10.5x")jest 10.5(the xsą ignorowane). Ponieważ obsługiwana jest tylko liczba dziesiętna, parseFloat("0x15")jest 0(ponieważ parsowanie kończy się na x). Zwraca, NaNjeśli nie można znaleźć analizowalnych cyfr.

  • Unary +, np. +str- (np. Niejawna konwersja) Konwertuje cały ciąg na liczbę przy użyciu liczb zmiennoprzecinkowych i standardowej notacji liczb JavaScript (tylko cyfry i kropka dziesiętna = dziesiętna; 0xprzedrostek = szesnastkowy; 0oprzedrostek = ósemkowy [ES2015 +]; niektóre implementacje go rozszerzają traktować wiodące 0jako ósemkowe, ale nie w trybie ścisłym). +"10x"to NaNdlatego, że niex jest ignorowane. jest , jest , jest , jest [ES2015 +]. Ma hascha: nie jest , jak można się spodziewać.+"10"10+"10.5"10.5+"0x15"21+"0o10"8+""0NaN

  • Number(str)- Dokładnie jak niejawna konwersja (np. Jak unary +powyżej), ale wolniej w niektórych implementacjach. (Nie żeby to miało znaczenie.)

TJ Crowder
źródło
8
Więc parseIntpo raz pierwszy używa toStringpierwszego argumentu? To miałoby sens.
evolutionxbox
16
@evolutionxbox: Tak, to pierwszy krok parseIntalgorytmu: ecma-international.org/ecma-262/7.0/…
TJ Crowder
5
Podejrzewam, że 123e-2daje, 1ponieważ 1.23najpierw zmienia się w , a następnie parsowanie zatrzymuje się w miejscu dziesiętnym?
ilkkachu
6
„Ludzie ciągle się potykają, nawet jeśli wiedzą o tym” -> czy tylko ja uważam, że to błąd? Na przykład robienie tego samego w Javie da ci za NumberFormatExceptionkażdym razem.
Wim Deblauwe
4
@ SvvenMarnach: Ta część parseInt(wymuszanie pierwszego argumentu na łańcuch) ma sens. Celem parseIntjest parsowanie łańcucha na liczbę całkowitą. Więc jeśli dasz mu coś, co nie jest łańcuchem, sensowne jest uzyskanie jego reprezentacji na początku. To, co potem robi , to cała „inna historia…
TJ Crowder,
54

Z tego samego powodu, który

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

W doc , parseIntpobiera ciąg. I

Jeśli ciąg nie jest ciągiem, jest on konwertowany na ciąg

Tak więc 16, 8lub '1foobar'jest najpierw konwertowany na ciąg.

Następnie

Jeśli parseIntnapotka znak, który nie jest liczbą w podanej podstawce, ignoruje go i wszystkie kolejne znaki

Oznacza to, że konwertuje tam, gdzie może. 6, 8I foobarsą ignorowane, a tylko to, co jest przed przekształca. Jeśli nic nie ma, NaNjest zwracane.

njzk2
źródło
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]

Kilka innych przykładów:

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
źródło