Skip to content
JavaScript js numbers 4 min read

Parsing Numbers

User input, query strings, file contents, and API responses all arrive as text. Before you can do arithmetic with them, you have to convert those strings into actual numbers — and JavaScript gives you several tools for the job, each with subtly different behavior. Picking the wrong one leads to silent bugs like "12px" becoming NaN or "08" parsing as 0. This page walks through Number(), parseInt(), parseFloat(), and the unary + operator so you always reach for the right one.

The four conversion tools

There are four idiomatic ways to turn a string into a number. They split into two camps: strict converters that reject anything that isn’t a clean number, and lenient parsers that read as much of the leading text as they can and ignore the rest.

ToolStyleReads partial input?Decimals?Radix control
Number(str)StrictNoYesNo (decimal + 0x/0o/0b prefixes)
+str (unary plus)StrictNoYesNo
parseInt(str, radix)LenientYesNo (integers only)Yes
parseFloat(str)LenientYesYesNo (decimal only)

Number() and unary +

Number() converts the entire string. If any non-numeric character remains (other than surrounding whitespace), you get NaN. The unary + operator is functionally identical for strings — it’s just shorter and reads nicely inline.

Number("42");       // 42
Number("3.14");     // 3.14
Number("  10  ");   // 10  (whitespace trimmed)
Number("");         // 0   (empty string!)
Number("12px");     // NaN (trailing junk rejected)
Number("0xFF");     // 255 (understands hex prefix)
Number("1e3");      // 1000 (scientific notation)

+"42";              // 42
+"3.14";            // 3.14
+true;              // 1

Because Number() is all-or-nothing, it’s the safest choice when you expect a clean numeric string and want a clear failure (NaN) otherwise.

Watch out: Number("") returns 0, not NaN. So does Number(null) and Number(" "). Always validate user input separately rather than trusting a falsy-to-zero conversion.

parseInt() and the radix argument

parseInt() reads an integer from the start of a string and stops at the first character it doesn’t understand. That makes it perfect for values with units like "16px".

The second argument, radix, tells parseInt() which number base to use. You should almost always pass it explicitly.

parseInt("16px");        // 16  (stops at "p")
parseInt("3.99");        // 3   (truncates, no rounding)
parseInt("   42 cats");  // 42
parseInt("FF", 16);      // 255 (hexadecimal)
parseInt("1010", 2);     // 10  (binary)
parseInt("hello");       // NaN (no leading digits)

Output:

16
3
42
255
10
NaN

Why pass the radix? Without it, parseInt() infers the base from the string’s prefix. Modern engines treat a leading 0 as decimal, but legacy behavior treated it as octal — and that ambiguity has caused real-world bugs (famously with zero-padded date strings).

parseInt("08");      // 8 in modern engines — but always specify the base
parseInt("08", 10);  // 8  (unambiguous, correct)
parseInt("0x1F");    // 31 (auto-detects hex prefix)

Tip: Make parseInt(str, 10) a habit. Linters like ESLint flag a missing radix (radix rule) precisely because the default behavior is surprising.

parseFloat()

parseFloat() is the lenient parser for decimal and scientific notation. Like parseInt(), it reads from the front and ignores trailing junk, but it keeps the fractional part.

parseFloat("3.14159");     // 3.14159
parseFloat("1.5rem");      // 1.5
parseFloat("  -0.75kg ");  // -0.75
parseFloat("6.022e23");    // 6.022e+23
parseFloat("12.34.56");    // 12.34  (stops at 2nd dot)
parseFloat(".5");          // 0.5

There is no radix parameter — parseFloat() is decimal only.

Choosing the right one

Use this decision guide:

  • Validating a complete numeric string (form field, config value) → Number() or unary +. You want NaN when the input is malformed.
  • Extracting a leading number from text with units ("24px", "1.5rem") → parseInt() for whole numbers, parseFloat() for decimals.
  • Parsing a non-decimal base (hex color, binary flag) → parseInt(str, 16) / parseInt(str, 2).
  • Shortest inline coercion → unary +.

Detecting failures with NaN

Every one of these returns NaN when it can’t produce a number. Since NaN is famously not equal to itself, never test with ===. Use Number.isNaN() (the strict, type-safe check) instead of the legacy global isNaN().

function toNumber(input) {
  const n = Number(input);
  return Number.isNaN(n) ? null : n;
}

console.log(toNumber("99.5"));  // 99.5
console.log(toNumber("12px"));  // null
console.log(toNumber("  "));    // 0  — Number trims whitespace
console.log(NaN === NaN);       // false — never compare with ===

Output:

99.5
null
0
false

Best practices

  • Reach for Number() (or unary +) when the whole string must be a valid number, and treat the NaN result as a validation failure.
  • Use parseInt() / parseFloat() only when you intentionally want to read a leading number and discard a trailing suffix.
  • Always pass an explicit radix to parseInt()parseInt(value, 10) for ordinary decimal integers.
  • Check results with Number.isNaN(), never with === NaN or the loose global isNaN().
  • Remember Number("") and Number(null) are 0; validate emptiness before converting if zero is a meaningful value.
  • For money and other precision-sensitive values, parse to a number only when you understand floating-point limits — see number precision below.
Last updated June 1, 2026
Was this helpful?