Number Base Converter

Binary, octal, decimal, hex and any base up to 36, with the bits and the float underneath

Bits

Choose a width to see and click individual bits.

Bitwise operations
The same bits as a floating point number

Bases belong to notation, not to numbers. Two hundred and fifty five is the same quantity in every row below; only the spelling changes.

binary   1111 1111
octal    377
decimal  255
hex      FF
base 36  73

The same number. Five ways of writing it down.

Hex and octal exist for one reason: they line up with binary without any arithmetic. One hex digit is exactly four bits and one octal digit is exactly three, so converting means reading the digits in groups. No division needed. Decimal has no such relationship with binary, and that is why a decimal-to-binary conversion is real work and a hex-to-binary one is not.

Why negative numbers look like enormous positive ones

In eight bits, signed:

  0000 0001    1
  0000 0000    0
  1111 1111   -1
  1111 1110   -2
  1000 0000   -128
  0111 1111    127

-1 is every bit set. Add 1 to it and the carry falls off
the top, leaving 0, which is exactly why it is written
that way: one adder does both signs.

Nobody sat down and picked two's complement. It is the encoding that makes subtraction free. Adding 1111 1111 to anything is the same as subtracting one, because the carry that would have gone into a ninth bit falls off the end. So the same adder circuit handles both signs, and there is no separate "is this negative" step anywhere in the hardware.

The cost is that the range is lopsided. Eight signed bits reach from -128 to 127, not -127 to 127, because there is only one zero and the spare pattern goes to the negative side. That is why Math.abs(-128) in a byte is still -128, and why the same thing is true of-2147483648 in a 32-bit integer.

Set a width above and the top bit is outlined. Click it and watch the value jump the whole width of the range.

Overflow is silent

Eight bits, unsigned:   255 + 1  =  0
Eight bits, signed:     127 + 1  = -128
Sixteen bits:         32767 + 1  = -32768   the Gandhi bug
Thirty-two bits: 2147483647 + 1  = -2147483648   19 January 2038

Nothing stops. There is no error and no signal; the value simply wraps and the program carries on with a wrong number. Civilization's famously pacifist-then-nuclear Gandhi is folklore and not fact, but the shape of the bug is real and everywhere: a counter that resets, a timestamp that goes backwards, a length that becomes negative and is then used as an array index.

The 2038 one is the same arithmetic on a bigger field. A signed 32-bit count of seconds since 1970 runs out at 03:14:07 UTC on 19 January 2038, and every system still storing time that way will wrap to 1901.

JavaScript stops counting at 2^53

A JavaScript number is a double, and a double holds every integer up to 9,007,199,254,740,991 exactly and then starts skipping. Past that,2**53 === 2**53 + 1 is true.

Which is why everything on this page is arbitrary precision. A converter built on parseInt and toString rounds, with no warning, exactly the values people bring to a base converter: 64-bit identifiers, hashes, register contents, timestamps in nanoseconds.

The other place it bites is bitwise operators. In JavaScript they all convert to 32-bit integers first, so 1 << 31 is-2147483648 and 1 << 32 is 1again. The operations above work on BigInt, at whatever width you set. That is why they disagree with a browser console.

What a floating point number actually is

Double, 64 bits:

 sign  exponent (11)          mantissa (52)
  0    01111111011   1001100110011001100110011001100110011001100110011010
  |         |                          |
  |         |                          + the fraction, with a leading 1 implied
  |         + 1019, less the bias of 1023, so x2^-4
  + positive

That is 0.1.

Three fields: a sign, an exponent, and a fraction. The value is the fraction, with a leading 1 assumed, multiplied by two to the power of the exponent. So a float is scientific notation with a fixed number of digits, except in binary.

And that is the whole problem. One tenth in binary is 0.0001100110011... repeating forever, exactly as one third in decimal is 0.333... repeating forever. Cut it off at 52 bits and what you have stored is close to a tenth and is not a tenth.

0.1 as a double, exactly:

  0.1000000000000000055511151231257827021181583404541015625

0.2 as a double, exactly:

  0.200000000000000011102230246251565404236316680908203125

Add them and the nearest double to the answer is

  0.3000000000000000444089209850062616169452667236328125

which prints as 0.30000000000000004.

Every one of those digits is exact. Binary fractions always terminate in decimal, because 2 divides 10, so there is no rounding involved in printing the true value. It only takes patience. The panel above will do it for any bit pattern you like.

The gap between numbers grows with the numbers

Floats are dense near zero and sparse a long way from it. Near 1, consecutive doubles are about 2.2 x 10^-16 apart. Near 2^52 they are 1 apart. Past 2^53 they are 2 apart, then 4, then 8.

So adding 1 to a large float can change nothing at all, and summing a million small values into a large accumulator loses most of them. It is the reason money is stored in pence as integers, and the reason0.1 + 0.2 !== 0.3 is the same fact wearing a different hat.

Two more corners, both visible in the panel above. Exponent bits all zero means a subnormal: the leading 1 is dropped and the value gets smaller in a coarser way. It is how floats fade towards zero instead of falling off a cliff. Exponent bits all one means infinity if the fraction is empty and NaN if it is not. So there are quadrillions of distinct NaN bit patterns, every one of them printing as NaN, and the fraction is where a signalling NaN keeps its payload.

Where the big bases are used

BaseDigitsWhere
160-9 A-FEverything: memory, colours, hashes, MAC addresses
32Crockford: no I, L, O or UULIDs, and anything meant to be read aloud
360-9 A-ZShort identifiers, and the shortest a number gets with plain alphanumerics
5836 plus lowercase, no 0 O I lBitcoin addresses. This tool does not offer it, since it is not positional in the usual way
64A-Z a-z 0-9 + /Base64. It encodes bytes, not numbers, and is a different thing entirely

Base 36 is the last base that fits inside the ASCII alphanumerics. Hence it turns up as the compact form of an ID so often. A 64-bit number is 20 digits in decimal and 13 in base 36.