What this tool does
This number base converter turns a value written in one base into the same value written in another, for every base from 2 to 36. Type a binary string and read it as decimal, paste a hexadecimal hash and see it in octal, or work in base 36 because you are shortening an identifier. Four readings — binary, octal, decimal and hexadecimal — are always shown together underneath, so you rarely have to run the conversion twice.
Three details separate it from the one-liner you could write in the console. Values are held as
arbitrary-precision integers, so nothing rounds. Prefixes are recognised, so a pasted 0x1F4 needs
no cleaning up. And digits can be grouped for reading — 1111 0000 in binary, DE AD BE EF in
hexadecimal — with the grouped form accepted back as input, which matters more than it sounds when
you are checking one string against another by eye.
Why parseInt quietly lies
The obvious way to do this in JavaScript is parseInt(text, 16), and it works right up until it
does not. Numbers in JavaScript are IEEE 754 doubles: 53 bits of mantissa, which means every integer
up to 9,007,199,254,740,991 is exact and everything above it is approximated to the nearest
representable value.
That is the dangerous part. parseInt does not throw, does not warn, and does not return anything
that looks wrong. Ask it for 18446744073709551617 and it hands back
18446744073709551616 — off by one, in a value long enough that nobody counts the digits. Snowflake
IDs, 64-bit hashes, database primary keys generated by a sequence: all of them live above the line
where doubles stop being exact, and all of them look fine until two of them collide.
The fix in code is BigInt, which has no upper bound and whose toString accepts any radix from 2
to 36. That is what runs here, which is why a 64-bit value converts and converts back to exactly
the digits you started with.
Two's complement, and why −1 is all ones
Negative numbers have no minus sign inside a machine word. Instead, the top bit carries a negative weight: in eight bits, the pattern is −128 + 64 + 32 + 16 + 8 + 4 + 2 + 1. Add one to 11111111 and it wraps to 00000000, which is exactly the behaviour you want from the value that sits one below zero — so 11111111 is −1.
Pick a width of 8, 16, 32 or 64 and the tool shows the stored bits along with both readings of them:
the signed one and the unsigned one. The same byte is 200 to a uint8_t and −56 to an int8_t, and
that single fact explains most of the confusion around a value that arrived over the wire looking
absurd.
When a number needs more bits than the width allows, the tool keeps the low bits and says so rather than refusing. Truncation is what the hardware does, and seeing the truncated result is usually how you work out that the width in your struct is too small.
When hex, when octal, when binary
Hexadecimal wins whenever the underlying thing is bytes. Two hex digits are exactly one byte, which is why colours, hashes, memory addresses and MAC addresses are all written that way. Grouping in pairs makes the byte boundaries visible.
Binary is for when the individual bits carry meaning — flags, masks, permission bits, a protocol header you are decoding by hand. Nobody reads a long binary string for its value; they read it for which positions are set.
Octal survives in exactly one common place: Unix file modes, where three bits map neatly onto one
digit and 755 means what it means. If that is why you are here, the
chmod calculator does the job directly, with the symbolic form beside the
number.
Two neighbours worth knowing about: the hash generator produces the long hexadecimal values this page is built to survive, and the Unix timestamp converter handles the other number that regularly overflows a naive parser.