Tools · Unit Conversion

Number Base Converter

Any base from 2 to 36, in either direction, with binary, octal, decimal and hexadecimal shown together every time. The arithmetic runs on arbitrary-precision integers, so a sixteen-digit hex value comes back exact rather than rounded to the nearest float.

Exact BigInt arithmetic, no 53-bit limitHow it worksInputs stay on this device
Your inputs

Convert between bases

Digits of the source base, upper or lower case. A leading minus sign is allowed, and spaces or underscores are ignored so you can group long values.

The base the number is already written in.

Binary, octal, decimal and hexadecimal are shown underneath whatever you pick here.

Your inputs are calculated locally and are not stored.
Base 16 (hexadecimal) to Base 2 (binary)11111111

FF in base 16 (hexadecimal) is 255 in decimal, and takes 8 bits to hold. Every conversion runs on arbitrary-precision integers, so a 64-bit value keeps all 64 bits.

Binary (base 2)
11111111
Octal (base 8)
377
Decimal (base 10)
255
Hexadecimal (base 16)
FF
Bit length
8 bits (1 byte)
Digits in base 2
8
Formula & methodology

Horner in, repeated division out, BigInt throughout

A positional number is a polynomial. The digits d₀…dₙ in base b stand for the sum of each digit times its place value, and the cheapest way to evaluate that is Horner’s method: sweep the digits left to right, multiplying the running total by the base and adding the next digit.

in: total = total × b + digit  •  out: digit = n mod b, n = ⌊n ÷ b⌋
b
The base — 2 for binary, 16 for hexadecimal
digit
The digit’s value: 0–9 then A–Z, so Z is 35
total
The value, held as a BigInt so it never overflows

Two decisions matter more than the formula. The first is that the value is a string on both sides and never a number. A JavaScript number is an IEEE 754 double with a 53-bit significand, so any value past 9,007,199,254,740,991 loses its low bits without complaint. That is well inside the range of the things people bring to a base converter: 64-bit identifiers, pointers, hash prefixes, permission masks. Holding the value as text and converting with BigInt is what makes the answer exact.

The second is that every digit is validated before any arithmetic runs. The obvious shortcut, parseInt, is float-backed and also stops silently at the first character it does not recognise — parseInt("1G7", 16)is 1, with no indication that two characters were thrown away. Here an illegal digit is rejected by name, and “F is not a digit in base 10” is a different message from “G is not a digit anywhere”.

Worked example

FF in base 16, converted to base 2

Going in: start at 0. The first digit F is worth 15, so the total is 0 × 16 + 15 = 15. The second F gives 15 × 16 + 15 = 255. Going out: 255 ÷ 2 is 127 remainder 1, then 63 r1, 31 r1, 15 r1, 7 r1, 3 r1, 1 r1, 0 r1 — eight ones, read bottom to top: 11111111.

The tidiness is not luck. 16 is 2⁴, so each hex digit maps onto exactly four binary digits with nothing left over: F is 1111, and FF is 1111 1111. That is the entire reason hexadecimal is the notation for bytes. A byte is eight bits, so it is always exactly two hex characters — 00 to FF, never one and never three — which is why a hex dump lines up in columns and an RGB colour is exactly six characters. Octal has the same property against three bits, which is why Unix file permissions are written 755 and not 493.

At the other end of the scale, try FFFFFFFFFFFFFFFF in base 16 to base 10. The answer is 18,446,744,073,709,551,615 — the largest unsigned 64-bit integer. Ask a converter built on JavaScript numbers and you get 18,446,744,073,709,552,000, which is wrong by 385 and looks entirely plausible.

Assumptions

What this converter assumes

  • The value is an integer. Fractions are out of scope: 0.1 in decimal is a repeating fraction in binary, so a fractional converter has to be told where to stop, and that is a different tool with a different input.
  • A negative value is a magnitude with a sign, not a bit pattern. Two’s-complement output would need a register width, which is a property of the machine rather than of the number.
  • Case carries no meaning, because in a 36-symbol alphabet it cannot. Input is folded to upper case; output is printed in upper case.
  • No prefixes are parsed. The source base is an explicit choice, so a leading 0x, 0b or 0o would have to either override that choice or be ignored — both surprising. Strip the prefix and pick the base.
  • Spaces and underscores are treated as grouping and removed, so 1111 0000 and 1111_0000 both work. Everything else that is not a legal digit is an error naming the character.
  • Bit length counts the bits needed for the magnitude, with the sign excluded, and zero needs none — the same convention as Python’s int.bit_length().
Common questions

Number base FAQ

Why is hexadecimal used for bytes and colours?

Because 16 is 2⁴, one hex digit is exactly four bits — no remainder, no carrying between digits. A byte is eight bits, so a byte is always exactly two hex digits: 00 through FF, never one character and never three. That fixed width is what makes hex dumps line up in columns, what makes an RGB colour exactly six characters, and what lets you read the top and bottom nibble of a byte straight off the page. Decimal has no such relationship — a byte is one, two or three decimal digits depending on its value.

Why does base 36 stop there?

A positional base needs one distinct symbol per digit value, and the conventional alphabet is the ten Arabic numerals followed by the twenty-six letters A–Z. Ten plus twenty-six is thirty-six, and there is no next symbol everyone agrees on. Base 58, base 62 and base 64 all exist, but each invents its own alphabet, its own case rules and its own padding, which makes them encodings rather than bases. JavaScript's own toString caps at 36 for exactly this reason.

How do I convert binary to decimal by hand?

Read left to right and use Horner's method: start at zero, and for each digit multiply what you have by the base and add the new digit. For 1010 in binary: 0×2+1 = 1, 1×2+0 = 2, 2×2+1 = 5, 5×2+0 = 10. It beats the place-value method because you never have to work out powers of two, and it is exactly what this converter does internally.

Why do some converters get long hexadecimal values wrong?

Because a JavaScript number is a 64-bit float with only 53 bits of significand. Anything past about 9.007 quadrillion loses its low bits silently: Number("0xFFFFFFFFFFFFFFFF") returns 18446744073709552000, not the correct 18446744073709551615. Nothing throws, nothing warns, and the answer is wrong in the last four digits. This tool converts with BigInt, which has no upper bound, so a 16-digit hex pointer or a 64-bit ID comes back exact.

How is a negative number handled?

As a magnitude with a minus sign in front: −255 in base 16 is −FF. It is deliberately not two's complement, because FFFFFFFF only means −1 if you already know the register is 32 bits wide. Width is a property of the machine, not of the number, and a converter that guessed would be wrong half the time.

Does upper or lower case matter?

No. In a base-36 alphabet, A and a are the same digit value — treating them as different would quietly create a 62-symbol system. Type either; results are printed in upper case.

Primary sources

Sources and review notes

  1. IEEE 754 — the floating-point standard behind the 53-bit significand that makes a number-typed converter lose digits
  2. ECMA-262 — BigInt, the arbitrary-precision integer type this tool converts with, and the 2–36 radix range of toString
  3. NIST Special Publication 811 — for the binary prefixes and the bit and byte symbol conventions used on this page

Positional notation does not change, so nothing on this page goes stale. The only moving part is the implementation: BigInt has been in the language since ES2020 and is supported by every browser this site targets, which is what makes exact conversion possible on the client with no server round trip.