By Docify

Positional BigInt vs Number.parseInt

• 6 min read

Number.parseInt stops at the first invalid digit and returns an IEEE-754 Number (exact integers only through 2^53 − 1). Number.parseInt("1012", 2) is 5. Number.parseInt("9007199254740993", 10) is 9007199254740992. The live converter reads bases 2–36 with Horner's method on BigInt (value = value * radix + digit) and rejects a bad digit instead of truncating. Output is BigInt.prototype.toString(radix). Not two's complement.

“Convert hex to decimal” is often implemented as Number.parseInt(s, 16). That function and positional notation are not the same algorithm. This page names the two failure modes — silent truncation and the 53-bit integer limit — and why the number base converter uses Horner's method on BigInt for every radix from 2 to 36.

Number.parseInt stops at the first bad digit

1012 in base 2 is not five

ECMA-262 parseInt (and Number.parseInt, the same function) reads a prefix of radix-R digits and ignores the rest. Number.parseInt("1012", 2) is 5 because it accepts 101 and stops at 2. Likewise Number.parseInt("12x3", 10) is 12, and Number.parseInt("10.5", 10) is 10. When the radix is 16, a leading 0x or 0X is a recognized prefix: Number.parseInt("0x10", 16) is 16. 0b and 0o are not special; Number.parseInt("0b10", 2) is 0.

parseInt("1012", 2) = 5 · converter throws on digit 2

The converter rejects the same strings

Horner's loop looks up each character in 0123456789abcdefghijklmnopqrstuvwxyz after toLowerCase(). A digit value must be less than the from-base, or the page throws Digit "2" is not valid in base 2. Hex 0x10 fails on x, not because prefixes are stripped. Load sample fills hex ff to decimal and does not run Convert. Copy writes the displayed string. Nothing is uploaded.

IEEE-754 cannot hold every integer

Exact integers end at 2^53 − 1

A JavaScript Number is IEEE-754 binary64. The significand is 53 bits, so Number.MAX_SAFE_INTEGER is 9007199254740991 (2^53 − 1). Past that, consecutive integers are not all representable: Number.parseInt("9007199254740993", 10) is 9007199254740992. The Number literal 9007199254740993 is already that even value, so === cannot detect the loss.

parseInt("9007199254740993", 10) = 9007199254740992

Hex 20000000000001 is 2^53 + 1

Fourteen hex digits reach 56 bits. 16^13 is 2^52 (4503599627370496), so 20000000000001 is 2 × 16^13 + 1 = 9007199254740993. Horner on BigInt keeps the odd integer 9007199254740993n. Number.parseInt("20000000000001", 16) is 9007199254740992. That is why this page does not parse with an IEEE-754 Number and does not emit Number.prototype.toString(radix).

BigInt() is not a radix parser

0xff works; ff throws

BigInt("0xff") is 255n. So do the binary and octal prefixes 0b and 0o. BigInt("ff") throws SyntaxError. There is no BigInt.parseInt(string, radix). A constructor call therefore cannot read the sample input ff in base 16, or a custom base-36 string, without a prefix the page also refuses.

BigInt("ff") → SyntaxError · Horner("ff", 16) → 255n

Horner: value = value * radix + digit

After trim and an optional leading minus (mathematical sign, not two's complement), each character is a digit value in 0–35. Hex ff is 0 × 16 + 15 = 15, then 15 × 16 + 15 = 255. Output is BigInt.prototype.toString(radix) (ECMA-262, lowercase 0-9a-z). Uppercase reformats those letters and does not convert again. At most 256 digits. Rejected: 0x / 0b / 0o prefixes, underscores, interior spaces, fractions, scientific notation, fixed-width two's complement, IEEE-754 dumps, and custom alphabets (not Base58 / Base62 / Base64).

Convert bases 2–36 with exact BigInt arithmetic

Horner parse, not Number.parseInt. Invalid digits throw. Nothing is uploaded.

Use Number Base Converter →

FAQ

Why not convert with Number.parseInt?
Number.parseInt (the same function as global parseInt, ECMA-262) stops at the first character that is not a digit of the given radix and returns whatever prefix it already read. Number.parseInt("1012", 2) is 5. Number.parseInt("0x10", 16) is 16 because a 0x prefix is allowed when the radix is 16. The live converter throws on those strings: 2 is not a binary digit, and x is not a hex digit. Convert is a button, not as-you-type.
What happens past 2^53 − 1?
A JavaScript Number is IEEE-754 binary64. Exact integers end at Number.MAX_SAFE_INTEGER, 9007199254740991 (2^53 − 1). Number.parseInt("9007199254740993", 10) is 9007199254740992. Hex 20000000000001 is 2 × 16^13 + 1 = 9007199254740993; Horner on BigInt keeps that odd integer, while Number.parseInt of the same hex string is 9007199254740992. Writing 9007199254740993 as a Number literal is already 9007199254740992, so === cannot show the gap.
Can BigInt() replace a radix parser?
No. The BigInt constructor accepts a decimal digit string or a 0x / 0b / 0o prefix (BigInt("0xff") is 255n). BigInt("ff") throws SyntaxError. There is no BigInt.parseInt(string, radix). Horner’s method on BigInt (value = value * radix + digit) is how this page reads an arbitrary base 2–36 string. The same prefixes the constructor wants are rejected here, so 0xff is not a hex input.
Is this two’s complement, Base58, or a float dump?
No. Digits are 0-9a-z (letters via toLowerCase()). An optional leading minus is a mathematical sign, not a two’s-complement wrap and not a bit width. Output is BigInt.prototype.toString(radix) (ECMA-262, lowercase); Uppercase reformats those letters. Load sample fills hex ff to decimal and does not run Convert. At most 256 digits. Not Base58 / Base62 / Base64, not IEEE-754 binary32/binary64, not Number.prototype.toString. Nothing is uploaded.

Related