Number Base Converter

Bases 2 to 36 through BigInt, so values past 2^53 stay exact, with the two's complement field alongside the signed value.

Enable JavaScript to customise; default output below.

Spaces, underscores and commas are ignored. A 0x, 0b or 0o prefix is accepted when it matches the base. The default is 2^53 + 1, which is where a JavaScript Number starts being wrong.

Live preview bases.txt
Input, base 10 (decimal)                  9007199254740993
Output, base 16 (hexadecimal)             20 00 00 00 00 00 01

Every common base
  base  2, binary                         10 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001
  base  8, octal                          400 000 000 000 000 001
  base 10, decimal                        9 007 199 254 740 993
  base 16, hexadecimal                    20 00 00 00 00 00 01
  base 32, base 32                        80 000 000 001
  base 36, base 36                        2g osa 7pa 2gx

With a prefix
  hex                                     0x20000000000001
  binary                                  0b100000000000000000000000000000000000000000000000000001
  octal                                   0o400000000000000001

Bits needed                               54
Bytes needed                              7
Past 2^53                                 yes, which is where a Number would start being wrong

In a fixed-width field, two's complement
   8 bit                                  does not fit
  16 bit                                  does not fit
  32 bit                                  does not fit
  64 bit                                  0000 0000 0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001  ·  0x0020000000000001

Every conversion here goes through BigInt, so it is exact at any size.
Most converters on the web use a JavaScript Number, which is a double:
past 2^53 the answer is quietly wrong, and 9007199254740993 comes back
as 9007199254740992 with nothing to say so.

The two's complement rows are the same as the binary for a positive
value, padded to the width. They differ for a negative number, which is
the case worth checking when you are reading a register or a packed
field.

Base 36 is where this stops, because that is the digits plus the Latin
alphabet. Base 64 is not a number base in this sense: it is a way of
encoding bytes as text, with its own alphabet and padding, and the
base64 encoder on this site is the tool for it.

Grouping is for reading and is not part of the value. Binary is grouped
in fours because that is one hex digit, and hex in twos because that is
one byte. Underscores, spaces and commas in the input are ignored for
the same reason.

In JavaScript, parseInt( "0x1f", 16 ) and Number( "0x1f" ) both work and
parseInt( "1f" ) returns 1, silently, because it stops at the first
character it cannot read. That is the other common source of a wrong
answer, and it is why this refuses a digit that is not valid in the base
rather than stopping early.

Output is valid and updates as you type.

Most base converters on the web share one bug: they use a JavaScript Number, which is a double. Past 2^53 the answer is quietly wrong. 9007199254740993 comes back as 9007199254740992, and nothing says so.

Everything here goes through BigInt, which is exact at any size. The default value in the field is 2^53 + 1 for that reason: paste it into another converter and watch the last digit change.

The second thing this does properly is negative numbers. A negative value has a signed representation in any base, and a fixed-width binary field holds its two’s complement, which is a different string. Both are printed, because which one you want depends on whether you are writing a value or reading a register.

How to use

  1. Put in the value. Spaces, underscores and commas are ignored, and a 0x, 0b or 0o prefix is accepted when it matches the base.
  2. Set the base it is in and the base you want.
  3. Read the fixed-width rows if the number is going into a field.

Example

-42 from decimal:

Every common base
  base  2, binary      -10 1010
  base  8, octal       -52
  base 10, decimal     -42
  base 16, hexadecimal -2a

Bits needed            6 for the magnitude
Bytes needed           1
Past 2^53              no

In a fixed-width field, two's complement
   8 bit               1101 0110  ·  0xD6  ·  214 unsigned
  16 bit               1111 1111 1101 0110  ·  0xFFD6  ·  65494 unsigned

-42 and 0xD6 are both correct and they answer different questions. The first is the value; the second is what an eight-bit register holds, which reads as 214 if you read it unsigned.

Pitfalls

parseInt stops at the first bad character. parseInt('1f') is 1, silently. That is the single most common source of a wrong conversion in JavaScript, and it is why this refuses a digit that is not valid in the base rather than returning a partial answer.

A Number is exact only to 2^53. That is 9,007,199,254,740,991. Above it, integers start skipping: 2^53 + 1 cannot be represented at all. Anything touching a 64-bit id, a nanosecond timestamp or a hash needs BigInt.

Two’s complement is width-dependent. -1 is 11111111 in eight bits and 1111111111111111 in sixteen. There is no such thing as “the binary of -1” without a width, which is why the rows are per width.

Base 36 is the end of the road. It is the ten digits plus twenty-six letters. Base 58 and base 64 are not number bases in this sense: they are byte encodings with their own alphabets and padding rules.

Case is not meaningful above base 10. FF and ff are the same value. The output uses lowercase, which is the convention in CSS and in most code, and uppercase for the two’s complement hex, which is the convention in a register dump.

Grouping is presentation. Binary in fours because that is one hex digit, hex in twos because that is one byte. Neither is part of the value and both make a 64-bit field readable.

Compatibility

Everything runs in the browser: nothing is uploaded and nothing is stored.

BigInt is in every browser since 2018 and has no upper limit but memory. The input is capped at 512 digits, which is well past anything readable and keeps the output sensible.

The round trip is checked in the test suite: a thirty-digit decimal written into every base from 2 to 36 and read back comes out identical. The 2^53 + 1 case is checked against a plain Number so the difference is visible in the tests rather than asserted in prose.

In JavaScript, BigInt('0x1f') works, BigInt('1f') throws, and parseInt does neither. If you are writing this yourself, BigInt with an explicit radix loop is the reliable route, and Number.prototype.toString( radix ) is fine for output up to base 36.

Frequently asked questions

Why is my hex value different from another converter?
If it is a large number, check the last few digits: the other converter is probably using a double. If it is negative, check whether you are comparing a signed value with a two’s complement field.
What is the 0x prefix for?
It tells a reader, and most parsers, that the digits are hexadecimal. 0b is binary and 0o is octal. They are not part of the number and this strips them on the way in and offers them on the way out.
How do I convert a colour?
A hex colour is three or four bytes of hexadecimal, so this will convert it, and the colour converter on this site is the better tool because it also handles the colour spaces.
Does it do fractions?
No. Fractional bases are a different problem, and doing them with BigInt means fixed-point arithmetic and a decision about precision that belongs in the tool that needs it.
What about base 64?
Base 64 encodes bytes as text rather than converting a number, so it needs the bytes and the padding rules. The base64 encoder on this site is the tool for it.
Weekly drops

New tools, when there are new tools

One email when something worth using ships. No schedule to fill, so no filler.

Your address goes nowhere else, and one click unsubscribes.