DevKitLab Logo DevKitLab
Number Bases / Hexadecimal / Binary

Hex, Binary, and Decimal: Reading Numbers Programmers Actually Use

A color is #FF5733. A file mode is chmod 755. A permission check is flags & 0x04. A constant is 0b1010, or 0xFF, or 255. Each looks like its own dialect, but they're one number in different clothes — and the fluency that makes all of it stop being friction is smaller than it looks: hex for bytes, binary for bits, decimal for humans.

You keep meeting the same numbers in different outfits. A color is #FF5733. A file mode is chmod 755. A permission check reads flags & 0x04. A constant is 0b1010, or 0xFF, or 255. Each notation looks like its own little dialect, and when you have to cross between them — is 0xFF really 255? does chmod 755 mean what I think? — you reach for a converter or count on your fingers. The fluency that makes all of this stop being friction is smaller than it looks, and it starts with one idea.

Here it is: hex, binary, and decimal are not three kinds of number — they’re three ways of writing the same number. 255, 0xFF, and 0b11111111 are one value in three notations, exactly as “fifteen,” “15,” and “XV” are one value in three notations. A number doesn’t have a base; a written number does. So the real skill isn’t conversion — a tool does that in a keystroke — it’s knowing why you’d switch: you pick the representation that makes the number’s structure visible. Hex when the number is really a pile of bytes (a color, a hash, an address). Binary when the individual bits carry meaning (flags, masks, hardware). Decimal when a human just needs to read a count.

This article spends a minute on what a base actually is — grouping, not identity — then on why one hex digit is exactly four bits, which makes hex a compact window onto binary. Then the places you actually meet these: colors, chmod, bitmasks. Then the bitwise operations that only make sense once you can see the bits, and the gotchas — signed numbers, byte order — that bite when you can’t.

The one idea: a base is how you group, not what the number is

Every number is a count of things. A base is just the size of the bucket you group them into before starting a new column. In decimal (base 10) each column is a power of ten: 255 = 2×100 + 5×10 + 5×1. In hex (base 16) each column is a power of sixteen, with digits 0–9 and then A–F for ten through fifteen: 0xFF = 15×16 + 15×1 = 255. In binary (base 2) each column is a power of two: 0b11111111 = 128+64+32+16+8+4+2+1 = 255. The same 255 every time. The value is what it is; the base only decides how it’s spelled — which is why “convert hex to decimal” never changes the number, only your view of it.

Hex: a compact window onto bytes

Why programmers reach for hex constantly comes down to one clean coincidence: 16 is 2⁴, so one hex digit is exactly four bits — a nibble — and two hex digits are exactly one byte (8 bits, 0–255, 0x000xFF). That alignment is the whole appeal. A byte written in decimal (77) tells you nothing about its bits; the same byte in hex (0x4D) splits cleanly into two nibbles, and each nibble maps to four bits you can read straight off. Try it on 0x2F: split it into the nibbles 2 and F, expand to the full byte 0010 1111, and add up the columns that are on — 32 + 8 + 4 + 2 + 1 = 47. So hex is really binary for humans — all the byte-and-bit structure, at a quarter of the digits. That’s why hashes, colors, memory addresses, and byte dumps are written in hex: they’re piles of bytes, and hex shows the bytes.

Binary: when the individual bits carry meaning

Sometimes you don’t want the compact view — you want the bits themselves, because each one means something. That’s binary, base 2, where every digit is a single bit: on or off, set or clear. You drop to binary when a number isn’t a quantity at all but a set of independent switches — a permission set, a feature-flags integer, a hardware register where bit 3 enables something and bit 6 means “error.” There, 13 as a decimal tells you nothing useful, but 0b1101 says bits 0, 2, and 3 are on — which is the actual information the number was carrying.

Where you actually meet them

Three everyday cases pin all of this down:

  • Colors are three bytes of hex. #FF5733 is FF 57 33 — red 0xFF (255), green 0x57 (87), blue 0x33 (51), each a byte from 0 to 255. #RRGGBBAA adds a fourth byte for alpha. Once you see a color as three bytes, #FFFFFF (every byte maxed → white) and #000000 (all zero → black) stop being magic.
  • Unix permissions are octal — a third base. chmod 755 isn’t decimal or hex; it’s base 8, and each digit is exactly three bits: r w x. 7 is 111 = read + write + execute; 5 is 101 = read + execute, no write. So 755 is rwx r-x r-x for owner / group / other. Octal survives here precisely because three-bits-per-digit lines up with the three permission bits. (Modern code writes it 0o755.)
  • Flags are bits you test with bitwise math. A set of options packed into one integer, each option a power of two so it owns exactly one bit: READ = 1 (0b001), WRITE = 2 (0b010), EXEC = 4 (0b100). You combine them with OR (READ | WRITE0b011), test one with AND (flags & WRITE is non-zero when the bit is set), and clear one by AND-ing with the inverse. The 0x04 in flags & 0x04 is just bit 2 wearing hex.

Bitwise operations: legible once you can see the bits

These work bit by bit, and they turn from cryptic to obvious the moment you write the operands in binary or hex:

  • AND (&) — 1 only where both are 1. Used to test or mask: x & 0xFF keeps the low byte and zeroes the rest.
  • OR (|) — 1 where either is 1. Used to set bits: x | 0x04 turns bit 2 on.
  • XOR (^) — 1 where the bits differ. Used to toggle bits, and in some simple checksum schemes.
  • NOT (~) — flips every bit within the value’s fixed width, so the result depends on how many bits wide the type is.
  • Shifts (<<, >>) — slide the bits left or right. For an unsigned value with no overflow, x << 1 is ×2 and x << 8 moves a value up a whole byte; for a non-negative value, x >> 4 drops the low nibble. (On signed values a right shift may sign-extend, and the exact rule is language-dependent — so lean on shifts for unsigned bit-twiddling.) Shifting is how you pack and unpack fields — (r << 16) | (g << 8) | b assembles a color from three separate bytes.

Try 0b1100 & 0b1010 and the answer is visibly 0b1000; try 12 & 10 in decimal and it’s a riddle. That’s the entire reason to switch representation before doing bit math.

The gotchas that bite

Two places where the tidy picture leaks, both worth knowing:

  • Signed vs unsigned, and two’s complement. The same bits can be read as an unsigned number or a signed one. Computers store negatives in two’s complement, where — in a fixed-width representation — the top bit acts as the sign, so in 8-bit two’s complement the byte 0xFF is 255 unsigned but -1 signed. This is the mechanism behind the classic 32-bit overflow: a signed 32-bit counter tops out at 0x7FFFFFFF, and one more increment would wrap past it toward a large negative — which is why many Unix systems that store time in a signed 32-bit integer hit the year-2038 problem. (How a language reacts to that overflow varies — some wrap, some throw, some leave it undefined.)
  • Byte order (endianness). When a number spans several bytes, they can be laid out big-endian (most significant first) or little-endian (least first). The value 0x01020304 is stored as 01 02 03 04 in a big-endian layout and 04 03 02 01 in a little-endian one — so a byte dump that looks “reversed” usually isn’t corrupt, just little-endian.

Two small conveniences round it out. The prefixes 0x (hex), 0b (binary), and 0o (octal) exist so a written number announces its base — 0x10 is 16, 0b10 is 2, 10 is ten. And leading zeros in hex or binary are just width: 0x0F and 0xF are the same value; the extra zero pads to a full byte for readability.

The quick reference

When a number in the wild looks unfamiliar, place it before you compute:

  1. Read the prefix. 0x hex, 0b binary, 0o (or a leading 0 in older C) octal, none → decimal. A # before six hex digits → an RGB color.
  2. Match the base to the structure. Bytes (colors, hashes, addresses) → hex. Independent bits (flags, masks, registers) → binary. A human-facing count → decimal. Permissions → octal.
  3. One hex digit = 4 bits, two = one byte. That single fact converts most hex to bits in your head, and back again.
  4. For bit math, switch to binary or hex first, then AND to test or mask, OR to set, XOR to toggle, shifts to move fields.
  5. Mind signedness and byte order when a value looks wildly wrong — a huge number that should be small is often a signed value read as unsigned, or bytes in the other endianness. (Those %XX pairs in a mangled URL are hex bytes too — the same skill reads them.)

For anything past mental arithmetic — a 64-bit mask, an unusual base, checking a two’s-complement value — a number base converter shows hex, decimal, binary, and octal side by side, so you see the same value in every notation at once. Underneath all of it is the one idea: these are one number in different clothes, and fluency is just knowing which outfit makes the structure visible.