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.
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.
Fix the highlighted fields to update the output.
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
- Put in the value. Spaces, underscores and commas are ignored, and a
0x,0bor0oprefix is accepted when it matches the base. - Set the base it is in and the base you want.
- 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?
What is the 0x prefix for?
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.