Pourquoi parseInt (8,3) == NaN et parseInt (16,3) == 1?

191

Je lis ceci mais je suis confus par ce qui est écrit dans le parseInt avec un chapitre d' argument de base

tableau des résultats de parseInt (_, 3)

Pourquoi est-ce que parseInt(8, 3)NaNet parseInt(16, 3)1?

AFAIK 8 et 16 ne sont pas des nombres de base 3, ils parseInt(16, 3)devraient donc revenir NaNaussi

les dix premiers nombres naturels en base 3

Devid Farinelli
la source
4
Encore un autre problème qui aurait été résolu par le typage statique (ou du moins pas la conversion implicite d'entiers en chaînes): P
Navin
4
@Navin Cela n'a rien à voir avec le typage statique ou dynamique (comme vous le notez vous-même). Le problème ici est faible par opposition à un typage fort.
Sven Marnach
12
Quand j'ai vu le titre de cette question, je me suis dit "c'est probablement parce que loljavascript". En voyant les réponses, je juge mon instinct fondamentalement correct.
Ben Millwood

Réponses:

373

C'est quelque chose que les gens trébuchent tout le temps, même lorsqu'ils en ont connaissance. :-) Vous voyez ceci pour la même raison parseInt("1abc")renvoie 1: parseInts'arrête au premier caractère invalide et renvoie tout ce qu'il a à ce point. S'il n'y a aucun caractère valide à analyser, il renvoie NaN.

parseInt(8, 3)signifie "analyser "8"en base 3" (notez que cela convertit le nombre 8en chaîne; détails dans la spécification ). Mais dans la base 3, les nombres à un chiffre sont juste 0, 1et 2. C'est comme lui demander d'analyser "9"en octal. Puisqu'il n'y avait aucun caractère valide, vous avez obtenu NaN.

parseInt(16, 3)lui demande d'analyser "16"en base 3. Puisqu'il peut analyser le 1, il le fait, puis il s'arrête au 6car il ne peut pas l'analyser. Alors ça revient 1.


Étant donné que cette question suscite beaucoup d'attention et pourrait être classée en bonne place dans les résultats de recherche, voici un aperçu des options de conversion de chaînes en nombres en JavaScript, avec leurs diverses particularités et applications (tirées d'une autre de mes réponses ici sur SO):

  • parseInt(str[, radix])- Convertit autant que possible le début de la chaîne en un nombre entier (entier), en ignorant les caractères supplémentaires à la fin. Ainsi parseInt("10x")est 10; le xest ignoré. Prise en charge un argument radix facultatif (base numérique), donc parseInt("15", 16)est 21( 15en hexadécimal). S'il n'y a pas de base, suppose décimal, sauf si la chaîne commence par 0x(ou 0X), auquel cas il les ignore et prend hex. (Certains navigateurs utilisaient pour traiter les chaînes commençant par 0octal; ce comportement n'a jamais été spécifié et a été spécifiquement interdit dans la spécification ES5.) Retourne NaNsi aucun chiffre analysable n'est trouvé.

  • parseFloat(str)- Comme parseInt, mais fait des nombres à virgule flottante et ne prend en charge que les nombres décimaux. Encore une fois sont ignorés, des caractères supplémentaires sur la chaîne parseFloat("10.5x")est 10.5(l' xest ignorée). Comme seul le nombre décimal est pris en charge, parseFloat("0x15")is 0(car l'analyse se termine au x). Renvoie NaNsi aucun chiffre analysable n'est trouvé.

  • Unaire +, par exemple +str- (Par exemple, conversion implicite) Convertit la chaîne entière en un nombre en utilisant la virgule flottante et la notation numérique standard de JavaScript (juste des chiffres et un point décimal = décimal; 0xpréfixe = hex; 0opréfixe = octal [ES2015 +]; certaines implémentations l'étendent pour traiter un interligne 0comme octal, mais pas en mode strict). +"10x"est NaNparce que le xn'est pas ignoré. +"10"est 10, +"10.5"est 10.5, +"0x15"est 21, +"0o10"est 8[ES2015 +]. A un piège: +""n'est 0pas NaNcomme on pourrait s'y attendre.

  • Number(str)- Exactement comme la conversion implicite (par exemple, comme l'unaire +ci-dessus), mais plus lent sur certaines implémentations. (Non pas que cela ait de l'importance.)

TJ Crowder
la source
8
Alors parseIntutilise toStringd'abord le premier argument? Cela aurait du sens.
evolutionxbox
16
@evolutionxbox: Ouais, c'est la première étape de l' parseIntalgorithme: ecma-international.org/ecma-262/7.0
TJ Crowder
5
Je suppose que 123e-2donne 1car il se transforme en 1.23premier, puis l'analyse s'arrête au point décimal?
ilkkachu
6
"C'est quelque chose que les gens trébuchent tout le temps, même quand ils le savent" -> suis-je le seul à penser que cela devrait être un bug? Faire la même chose en Java par exemple vous donnera un à NumberFormatExceptionchaque fois.
Wim Deblauwe
4
@SvenMarnach: Cette partie de parseInt(forcer le premier argument à la chaîne) a du sens. Le but de parseIntest d' analyser une chaîne en un nombre entier. Donc, si vous lui donnez quelque chose qui n'est pas une chaîne, il est logique d'en obtenir la représentation sous forme de chaîne. Ce qu'il fait après cela, c'est toute une autre histoire ...
TJ Crowder
54

Pour la même raison que

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

Dans la doc , parseIntprend une chaîne. Et

Si la chaîne n'est pas une chaîne, elle est convertie en chaîne

Donc 16, 8ou '1foobar'est d'abord converti en chaîne.

ensuite

Si parseIntrencontre un caractère qui n'est pas un chiffre dans la base spécifiée, il l'ignore ainsi que tous les caractères suivants

Cela signifie qu'il se convertit là où il le peut. Le 6, 8et foobarsont ignorés, et que ce qui est devant est converti. S'il n'y a rien, NaNest retourné.

njzk2
la source
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]

Quelques autres exemples:

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
la source