Hex, binary, and why programmers count differently.
Decimal, hex, binary, octal — they look like different number systems, but they're the same numbers wearing different clothes. Here's the single idea behind every positional base, why programmers reach for 16 and 2, and the precision bug that quietly corrupts large hexadecimal values.
Ask someone how many fingers they have and they'll say ten — but that "10" is a choice, not a fact about quantity. We count in tens because we have ten fingers, and the notation we built around that habit is so familiar it feels like the numbers themselves. Step into programming and you immediately meet 16s and 2s, which feel alien for about a week and then become second nature. The trick is realizing they're all the same machine with a different dial setting.
One idea: digits times powers.
A positional number is a sum of each digit multiplied by a power of the base, counting up from the right starting at zero. In decimal (base 10), the number 255 means:
255 = 2×10² + 5×10¹ + 5×10⁰
= 200 + 50 + 5
Change the base and only the multipliers change. The same quantity in hexadecimal (base 16) is FF:
FF = 15×16¹ + 15×16⁰
= 240 + 15 = 255
And in binary (base 2) it's 11111111 — eight ones, each a power of two from 128 down to 1, which add up to 255. Three notations, one value. The base is just how many distinct digits you have before you run out and have to carry into the next column. Base 10 has ten (0–9); base 2 has two (0–1); base 16 has sixteen.
The whole concept: a number is Σ digit × base^position. Pick a different base and you've only changed the size of each step between columns. The thing being counted never moves.
Why programmers love hex and binary.
Computers are physically binary: every value is ultimately a row of bits, each on or off. So binary is the machine's native tongue — but it's miserable to read. A single byte is eight bits, and 01101011 tells a human almost nothing at a glance.
Hexadecimal is the fix, and the reason is a clean coincidence: one hex digit is exactly four bits, because 16 = 2⁴. That means every byte (8 bits) is precisely two hex digits, with no awkward remainder. 11111111 becomes FF; 0110 1011 becomes 6B. Hex is just binary, grouped into readable nibbles. This is why colors are #FF6B9A, why memory addresses and hashes are written in hex, and why 0xFF is the friendly way to say "all eight bits on."
Octal (base 8) is the same idea with three bits per digit (8 = 2³). It's mostly a historical holdover now, surviving in one famous place: Unix file permissions, where three bits per class map perfectly to one octal digit per class.
| Base | Digits | Bits per digit | Seen in |
|---|---|---|---|
| 2 (binary) | 0–1 | 1 | the hardware itself, bitmasks |
| 8 (octal) | 0–7 | 3 | file permissions (chmod) |
| 10 (decimal) | 0–9 | ~3.32 | humans |
| 16 (hex) | 0–9, a–f | 4 | colors, addresses, hashes, bytes |
Converting by hand (and why you shouldn't).
To convert a value out of decimal, you repeatedly divide by the target base and read the remainders bottom-to-top. For 255 into hex: 255 ÷ 16 = 15 remainder 15 (F), then 15 ÷ 16 = 0 remainder 15 (F) → FF. To convert into decimal, you do the digits-times-powers sum from the first section.
It's worth doing once to feel how it works. After that, do it in a tool — not because the arithmetic is hard, but because the failure mode is silent. Transpose one digit converting a 64-bit hash by hand and nothing complains; you just have a wrong number. Machines don't fat-finger the carry.
Bases above ten borrow letters.
Hex needs sixteen distinct digits but we only have ten numerals, so it borrows the first six letters: a=10, b=11, … f=15. Push further and you reach base 36, which uses all ten digits plus the whole alphabet (z=35). Base 36 is the densest base you can write with the standard alphanumeric set, which is why it shows up in short URLs, IDs, and other places where you want to pack a big number into few characters. zz in base 36 is 35×36 + 35 = 1,295 — two characters covering what decimal needs four for.
(You can go past 36 with schemes like Base58 or Base64, but those add symbols or drop confusable characters and aren't "positional bases" in the same clean sense — they're encodings with their own rules.)
The large-hex precision trap.
Here's the bug that motivates doing this carefully. In JavaScript — and many languages' default number type — integers are stored as 64-bit floating point, which can only represent whole numbers exactly up to 2⁵³ (about 9 quadrillion). Past that, conversions silently round:
parseInt("20000000000001", 16)
// → 9007199254740992 (should end in ...993)
The input was 2⁵³ + 1, and the answer came back as 2⁵³ — off by one, with no error. For anything that carries large hex or decimal values exactly — 64-bit IDs, hashes, database keys, cryptographic material — that rounding is corruption. The fix is an arbitrary-precision integer type: JavaScript's BigInt, Python's native big ints, Java's BigInteger. They hold as many digits as you have memory for and never round. A good base converter uses one under the hood, so a 256-bit number round-trips through every base unchanged.
The trap in one line: default numeric types lie above 2⁵³. If a value might be large and must be exact, convert it with big-integer math, not floating point.
Takeaways.
The thing to remember: every base is digits times powers; programmers use hex because one digit is exactly four bits (and octal because it's three); bases up to 36 borrow letters; and large values need arbitrary-precision math or they round silently. The numbers are identical — only the notation changes.
Once "base" stops being a mysterious property and becomes "how big each column step is," hex and binary lose their menace. They're just decimal's coworkers, better suited to the job of describing a machine that, underneath everything, is only ever counting in twos.
Convert between any bases exactly.
The Base Converter turns a number between binary, octal, decimal, hex, and any radix from 2 to 36 — live, and with arbitrary-precision BigInt so even 256-bit values convert without a rounding error. Entirely in your browser, nothing uploaded.