Why Hexadecimal Won and Base 10 Didn't
Computers work in binary. So why does every debugger, every colour code, every memory address display in hexadecimal rather than binary or decimal?
The answer is a property most people never articulate: 16 is a power of 2, and 10 isn't.
That single fact determines everything about how these bases interact.
The Alignment Property
When one base is a power of another, conversion between them is purely positional — you can do it by grouping digits, with no arithmetic at all.
16 = 2⁴, so each hex digit maps to exactly four binary digits:
Hex: A 3 F 7
Binary: 1010 0011 1111 0111
No carrying, no division, no remainders. Each hex digit is independently four bits. You can convert either direction by reading in groups of four.
8 = 2³, so each octal digit maps to exactly three bits:
Octal: 7 5 2
Binary: 111 101 010
Decimal has no such property. 10 isn't a power of 2, so converting between decimal and binary requires repeated division and produces no digit-level correspondence. The value 200 in decimal tells you nothing about its bit pattern until you compute it.
This is why hex is the debugger's base. Reading 0x3F you immediately know the bits are 0011 1111. Reading 63 you know nothing until you convert.
What Hex Reveals That Decimal Hides
Byte boundaries
Two hex digits are exactly one byte. Always. This makes byte-oriented data trivially readable:
Hex: 48 65 6C 6C 6F
Decimal: 72 101 108 108 111
The hex version has uniform two-character columns aligned to byte boundaries. The decimal version has variable widths and no visual alignment. Scanning a memory dump in decimal is genuinely harder for this reason alone.
Bit patterns and masks
0xFF = 1111 1111 all bits set in a byte
0xF0 = 1111 0000 high nibble
0x0F = 0000 1111 low nibble
0x80 = 1000 0000 most significant bit only
0x01 = 0000 0001 least significant bit only
0xAA = 1010 1010 alternating
0x55 = 0101 0101 alternating, inverted
Every one of these is recognisable at a glance in hex. In decimal — 255, 240, 15, 128, 1, 170, 85 — only a couple are memorable, and none tell you their bit pattern.
The 0xAA / 0x55 pair is a standard memory test pattern precisely because it exercises every bit in both states, and it's obvious in hex that it does.
Powers of two
0x100 = 256
0x400 = 1,024 (1 KiB)
0x1000 = 4,096 (page size on most architectures)
0x10000 = 65,536
Round numbers in hex are powers of two. A memory address ending in three zeros in hex is page-aligned. A size of 0x100000 is exactly 1 MiB. This alignment information is visible in hex and invisible in decimal.
Reading a Value in Its Natural Base
The Base Converter translates between binary, octal, decimal and hexadecimal:
- Enter your value.
- Select the source base.
- Read the equivalents.
The habit worth developing is asking which base makes this value's structure visible.
Use hex for: memory addresses, byte values, colour codes, bitmasks, hashes, MAC addresses, file signatures, anything byte-oriented.
Use binary for: individual bit flags, small bit patterns, understanding a mask you're constructing. Binary gets unwieldy fast — a 32-bit value is 32 characters.
Use octal for: Unix file permissions, essentially. 0755 groups cleanly into three-bit read/write/execute triples, which is exactly what the permission bits are. Outside that context, octal is largely historical.
Use decimal for: quantities humans reason about. Counts, sizes shown to users, arithmetic.
The Octal Trap
A leading zero means octal in C, and in several languages that inherited its syntax.
int a = 100; // one hundred
int b = 0100; // sixty-four
This catches people writing zero-padded values — dates, IDs, codes. 08 and 09 aren't even valid octal digits, so they produce a compile error, which is at least a loud failure. 010 compiles fine and means 8.
JavaScript deprecated legacy octal literals in strict mode for this reason, and introduced the explicit 0o prefix. Python 3 requires 0o and rejects a bare leading zero.
Modern prefixes worth knowing:
0b1010 binary
0o755 octal
0x3FF hexadecimal
1_000_000 digit separator (many languages)
The underscore separator is a small quality-of-life feature that's widely supported now and worth using for long literals — 0xFF_FF_FF_FF is easier to read than the unbroken form.
Where You'll Meet Each Base
Colour codes. #FF5733 is three bytes: red FF, green 57, blue 33. Each channel is one byte, 0–255, and hex makes the channel boundaries visually obvious. The eight-digit form adds an alpha byte.
MAC addresses. 00:1A:2B:3C:4D:5E — six bytes, hex, colon-separated. The first three bytes identify the manufacturer.
IPv6. Groups of four hex digits, each group being two bytes. IPv4 uses decimal because its four bytes are small enough to read individually; IPv6's 16 bytes would be unreadable that way.
File signatures. 89 50 4E 47 for PNG, FF D8 FF for JPEG, 25 50 44 46 for PDF. Reading these in hex lets you spot the ASCII within them — 50 4E 47 is "PNG".
Unicode code points. U+1F600 — always written in hex, because the plane and block structure aligns to hex boundaries.
Unix permissions. chmod 755 — octal, three bits per digit, one digit each for owner, group and others.
Assembly and disassembly. Everything in hex, because everything is byte- and address-oriented.
Endianness
One thing hex reveals that catches people out: the same 32-bit value can appear in memory in two different byte orders.
Value: 0x12345678
Little-endian: 78 56 34 12
Big-endian: 12 34 56 78
Little-endian stores the least significant byte first. x86, x86-64 and most ARM configurations use it.
Big-endian stores the most significant byte first. Network protocols use it — hence "network byte order."
Reading a memory dump on a little-endian machine, multi-byte values appear reversed relative to how you'd write them. A value that looks like 78 56 34 12 in a hex dump is 0x12345678.
This is why file formats and network protocols specify byte order explicitly, and why functions like htonl and ntohl exist. A value written by a big-endian system and read by a little-endian one without conversion is byte-swapped garbage.
Two's Complement
Negative integers have a representation that's much clearer in hex than decimal.
In 8-bit two's complement:
1 = 0x01
0 = 0x00
-1 = 0xFF
-2 = 0xFE
-128 = 0x80
-1 is all bits set. This is consistent across widths: 0xFFFF in 16-bit, 0xFFFFFFFF in 32-bit.
Seeing 0xFFFFFFFF in a debugger is a strong signal you're looking at either -1 as a signed value or the maximum unsigned value, depending on interpretation. Seeing 4294967295 is less immediately informative.
The wraparound behaviour also becomes obvious: adding 1 to 0xFF in an 8-bit register gives 0x00. In decimal, 255 + 1 = 0 looks like an error rather than a defined overflow.
Practical Tips
Convert to hex when debugging anything byte-oriented. Memory, network packets, file contents, hashes.
Convert to binary when working with individual flags. A bitmask is easier to reason about bit by bit.
Never use a leading zero on a decimal literal. It's octal in more languages than you'd expect.
Check byte order when reading raw data or comparing values across systems.
Watch for 0xFF... patterns — they usually mean -1, an uninitialised value, or an error sentinel.
Group long hex values with underscores or spaces at byte boundaries for readability.
FAQ
Why do computers use hexadecimal instead of decimal? Because 16 is a power of 2, each hex digit maps to exactly four bits. That makes conversion positional and byte boundaries visible, neither of which is true for decimal.
Why does one hex digit equal four bits? Because 2⁴ = 16, so a single hex digit can represent exactly the 16 values that four bits can hold.
What does the 0x prefix mean?
It marks a hexadecimal literal. 0b marks binary and 0o marks octal in most modern languages.
Why is 0755 different from 755?
A leading zero denotes octal in C and languages derived from it. 0755 is 493 in decimal.
What is endianness? The order in which bytes of a multi-byte value are stored. Little-endian stores the least significant byte first; big-endian the opposite.
Why is -1 represented as 0xFF?
Two's complement representation. In eight bits, all ones is -1, and the pattern extends to wider types as 0xFFFF, 0xFFFFFFFF and so on.
The Takeaway
Base choice isn't cosmetic — it determines what structure is visible. Hex exists because 16 is a power of 2, which makes bit patterns, byte boundaries and memory alignment readable at a glance. Converting a value into the base that matches its structure is often the whole debugging step.
Convert between binary, octal, decimal and hexadecimal free with the Base Converter at sadiqbd.com — no sign-up, instant results.