为啥 parseInt(8,3) == NaN 和 parseInt(16,3) == 1?

Posted

技术标签:

【中文标题】为啥 parseInt(8,3) == NaN 和 parseInt(16,3) == 1?【英文标题】:Why is it that parseInt(8,3) == NaN and parseInt(16,3) == 1?为什么 parseInt(8,3) == NaN 和 parseInt(16,3) == 1? 【发布时间】:2017-01-01 23:50:59 【问题描述】:

我正在阅读 this,但我对 parseInt 中使用基数参数一章中所写的内容感到困惑

为什么parseInt(8, 3)NaNparseInt(16, 3)1

AFAIK 8 和 16 不是基数为 3 的数字,所以 parseInt(16, 3) 也应该返回 NaN

【问题讨论】:

另一个可以通过静态类型解决的问题(或者至少不会将整数隐式转换为字符串):P @Navin 这与静态类型与动态类型无关(正如您自己所指出的那样)。这里的问题是弱类型而不是强类型。 当我看到这个问题的标题时,我心想,“可能是因为loljavascript”。看到答案,我判断我的直觉基本上是正确的。 【参考方案1】:

这是人们一直都会遇到的事情,即使他们知道这一点。 :-) 你看到这个的原因与parseInt("1abc") 返回 1 的原因相同:parseInt 在第一个无效字符处停止并返回它在该点的任何内容。如果没有要解析的有效字符,则返回NaN

parseInt(8, 3) 表示“以 3 为基数解析 "8"”(注意它将数字 8 转换为字符串;details in the spec)。但在基数 3 中,个位数只是 012。这就像要求它以八进制解析"9"。因为没有没有个有效字符,所以你得到了NaN

parseInt(16, 3) 要求它解析基数为 3 的 "16"。由于它可以解析 1,它可以解析,然后它在 6 处停止,因为它无法解析它。所以它返回1


由于这个问题引起了很多关注,并且可能在搜索结果中排名很高,因此这里列出了在 JavaScript 中将字符串转换为数字的选项,以及它们的各种特性和应用程序(摘自我在 SO 上的另一个答案) :

parseInt(str[, radix]) - 将字符串的开头尽可能多地转换为整数(整数),忽略末尾的额外字符。所以parseInt("10x")10x 被忽略。支持可选的基数(数字基数)参数,因此parseInt("15", 16)2115 十六进制)。如果没有基数,则假定为十进制,除非字符串以 0x(或 0X)开头,在这种情况下,它会跳过这些并假定为十六进制。 (一些浏览器过去会将以0 开头的字符串视为八进制;这种行为从未被指定,并且在 ES5 规范中是 specifically disallowed。) 如果找不到可解析的数字,则返回 NaN

parseFloat(str) - 与parseInt 类似,但处理浮点数且仅支持十进制。字符串上的额外字符再次被忽略,所以parseFloat("10.5x")10.5x 被忽略)。由于只支持十进制,parseFloat("0x15")0(因为解析在x 结束)。如果找不到可解析的数字,则返回 NaN

一元+,例如+str - (例如,隐式转换) 使用浮点和 JavaScript 的标准数字表示法将 整个 字符串转换为数字(只有数字和小数点 = 小数;@ 987654362@ prefix = hex; 0o prefix = octal [ES2015+]; some 实现将其扩展为将前导 0 视为八进制,但不是在严格模式下)。 +"10x"NaN 因为 x忽略。 +"10"10+"10.5"10.5+"0x15"21+"0o10"8 [ES2015+]。有一个问题:+""0,而不是您所期望的 NaN

Number(str) - 完全像隐式转换(例如,像上面的一元 +),但在某些实现上较慢。 (这可能并不重要。)

【讨论】:

所以parseInt首先在第一个参数上使用toString?这是有道理的。 @evolutionxbox:是的,这是parseInt 算法的第一步:ecma-international.org/ecma-262/7.0/… 我想123e-21 因为它先变成1.23,然后解析在小数点处停止? “这是人们一直在纠结的事情,即使他们知道这一点” -> 只有我认为这应该是一个错误吗?例如,在 Java 中做同样的事情,每次都会给你一个NumberFormatException @SvenMarnach:parseInt 部分(将第一个参数强制为字符串)是有道理的。 parseInt 的目的是将字符串解析为整数。所以如果你给它一些不是字符串的东西,那么让它的字符串表示开始是有意义的。 之后它做了什么,那是一个完整的“另一个故事......【参考方案2】:

出于同样的原因

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

在the doc 中,parseInt 接受一个字符串。和

如果string不是字符串,则转换为字符串

所以168'1foobar'首先转换为字符串。

然后

如果parseInt 遇到的字符不是指定基数中的数字,则忽略它和所有后续字符

意味着它可以转换到它可以转换的地方。 68foobar 将被忽略,仅转换之前的内容。如果没有,则返回NaN

【讨论】:

【参考方案3】:
/***** 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 

【讨论】:

以上是关于为啥 parseInt(8,3) == NaN 和 parseInt(16,3) == 1?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 parseInt(key) === NaN 总是评估为 false

为啥 parseInt("0x") 返回 NaN [重复]

为啥我在使用 parseINt 或 undefined 时得到 NaN?

为啥 NaN 大于 JavaScript 中的任何数字? [复制]

为啥“valueAsNumber”返回 NaN 作为值?

深入了解parseInt