javascriptroom blog

Why Does `parseInt(8, 3)` Return NaN But `parseInt(16, 3)` Return 1? JavaScript Radix Behavior Explained

JavaScript’s parseInt function is a workhorse for converting strings to integers, but its behavior can be surprisingly tricky—especially when the radix parameter is involved. If you’ve ever stumbled upon results like parseInt(8, 3) === NaN and parseInt(16, 3) === 1 and wondered, “Why?”, you’re not alone. These examples highlight a critical but often misunderstood aspect of how parseInt parses numbers with different bases (radices).

In this blog, we’ll demystify parseInt’s radix behavior, break down exactly why these two examples produce such different results, and equip you with best practices to avoid common pitfalls. Whether you’re a beginner or a seasoned developer, understanding this will save you from bugs and deepen your grasp of JavaScript’s type conversion logic.

2025-11

Table of Contents#

  1. How parseInt Works: A Quick Refresher
  2. Understanding Radix: What Is It, and Why Does It Matter?
  3. Case Study 1: Why parseInt(8, 3) Returns NaN
  4. Case Study 2: Why parseInt(16, 3) Returns 1
  5. The Key Difference: Valid vs. Invalid Leading Digits
  6. Common Pitfalls with parseInt and Radix
  7. Best Practices for Using parseInt
  8. Conclusion
  9. References

How parseInt Works: A Quick Refresher#

Before diving into radix, let’s recap how parseInt functions. The syntax is:

parseInt(string, radix);
  • string: The value to parse (coerced to a string if not already one).
  • radix (optional): An integer between 2 and 36 representing the base of the numeral system to use. If omitted, parseInt guesses the base (more on this later—but never rely on this!).

Core Behavior:
parseInt parses the string from left to right, extracting digits that are valid for the specified radix. It stops parsing at the first invalid character (including non-digit characters, or digits larger than the radix allows). If no valid digits are found (even in the first position), it returns NaN. Otherwise, it converts the valid prefix of the string to a decimal (base 10) integer.

Understanding Radix: What Is It, and Why Does It Matter?#

The radix (or base) defines the numeral system used to interpret the string argument. For example:

  • Radix 10: Decimal (digits 0-9).
  • Radix 2: Binary (digits 0-1).
  • Radix 16: Hexadecimal (digits 0-9, A-F).

For a given radix r, valid digits are 0 to r-1. For example:

  • Radix 3: Valid digits are 0, 1, 2 (since 3-1 = 2). Any digit ≥ 3 is invalid.

This is critical for our examples: both use radix = 3, so only 0, 1, and 2 are valid digits.

Case Study 1: Why parseInt(8, 3) Returns NaN#

Let’s dissect parseInt(8, 3) step by step:

Step 1: Coerce the Input to a String#

The first argument is 8, which JavaScript coerces to the string "8".

Step 2: Check for Valid Digits in Radix 3#

Radix 3 only allows digits 0, 1, 2. The first (and only) character in "8" is "8", which is invalid for radix 3 (since 8 > 2).

Step 3: No Valid Digits → Return NaN#

Since parseInt cannot find any valid digits to parse (the first character is already invalid), it returns NaN.

Case Study 2: Why parseInt(16, 3) Returns 1#

Now let’s analyze parseInt(16, 3):

Step 1: Coerce the Input to a String#

The first argument is 16, which coerces to the string "16".

Step 2: Parse Left-to-Right, Stopping at Invalid Digits#

  • First character: "1". In radix 3, 1 is a valid digit (since 1 ≤ 2).
  • Second character: "6". In radix 3, 6 is invalid (since 6 > 2).

parseInt stops parsing at the first invalid character ("6") and keeps only the valid prefix: "1".

Step 3: Convert the Valid Prefix to Decimal#

Now, convert "1" (from radix 3) to decimal:

  • For a single digit d in radix r, the decimal value is d * r⁰.
  • Here: 1 * 3⁰ = 1 * 1 = 1.

Thus, parseInt(16, 3) returns 1.

The Key Difference: Valid vs. Invalid Leading Digits#

The discrepancy between parseInt(8, 3) (returns NaN) and parseInt(16, 3) (returns 1) boils down to whether the first character is a valid digit for the radix:

  • parseInt(8, 3): The first character ("8") is invalid. No valid digits are parsed → NaN.
  • parseInt(16, 3): The first character ("1") is valid. parseInt parses it, then stops at the next invalid character ("6"). The valid prefix ("1") is converted to decimal → 1.

Common Pitfalls with parseInt and Radix#

Even experienced developers trip over parseInt’s behavior. Here are key pitfalls to avoid:

1. Forgetting to Specify the Radix#

If you omit the radix, parseInt guesses the base:

  • If the string starts with "0x", it uses radix 16.
  • If the string starts with "0", it used to use radix 8 (octal) in older browsers (now deprecated, but still risky).
  • Otherwise, it defaults to radix 10.

Example: parseInt("08") may return 0 (octal) in legacy code, but 8 in modern browsers. Always specify the radix!

2. Ignoring Invalid Digits Mid-String#

As we saw with parseInt(16, 3), parseInt stops at the first invalid digit but still parses the valid prefix. This can lead to unexpected results if you assume the entire string is valid.

3. Non-String Inputs#

parseInt coerces non-string inputs to strings, which can cause confusion. For example:

parseInt(true, 3); // Coerces to "true" → first character "t" is invalid → NaN

Best Practices for Using parseInt#

To avoid bugs with parseInt, follow these practices:

  1. Always Specify the Radix: Explicitly pass a radix (e.g., parseInt(str, 10) for decimal) to avoid ambiguous behavior.
  2. Validate Inputs: Check that the string contains only valid digits for the radix before parsing (e.g., use a regex like /^[0-2]+$/ for radix 3).
  3. Beware of Coercion: If the input isn’t a string, explicitly convert it first (e.g., String(num)) to make behavior predictable.
  4. Handle NaN Gracefully: Check if the result is NaN and handle invalid inputs (e.g., if (isNaN(result)) { throw new Error("Invalid number"); }).

Conclusion#

The mystery of parseInt(8, 3) returning NaN and parseInt(16, 3) returning 1 is solved by understanding how parseInt parses strings with a specified radix:

  • It parses left-to-right, stopping at the first invalid digit.
  • If no valid digits are found (even in the first position), it returns NaN.
  • Otherwise, it converts the valid prefix to decimal.

By mastering radix behavior and following best practices like specifying the radix, you’ll avoid common parseInt pitfalls and write more robust JavaScript code.

References#