Polyatic

Number Bases Explained

The number 156, the byte 9C, and the bit pattern 1001 1100 are the same quantity wearing three different outfits. Decimal, binary, octal and hexadecimal are not four different numbers — they are four ways of spelling one number, and the only thing that changes between them is how many distinct digit symbols you allow yourself. This guide builds that idea from the ground up: what positional notation really means, why digital electronics forced us into binary, why programmers read raw bytes in hex, one value worked out by hand across all four bases, how hex shows up in #RRGGBB colors and hex dumps, and — the part that trips everyone up — how a fixed-width computer stores negative numbers with two's-complement.

Positional notation: every digit has a place value

Start with the system you already know. When you write 156 in decimal (base 10), you do not read three unrelated symbols — you read a sum weighted by position. Each place is worth ten times the place to its right, because ten is the base:

156 in base 10 1 × 10² = 1 × 100 = 100 5 × 10¹ = 5 × 10 = 50 6 × 10⁰ = 6 × 1 = 6 ───── 156

That is the whole trick, and it generalises. A base (or radix) is nothing more than how many distinct digit symbols the system uses — and therefore what each position is worth. In base b the rightmost digit counts ones (b0), the next counts bs (b1), the next b², and so on. Base 10 uses ten symbols (0–9) and place values 1, 10, 100, …; base 2 uses two (0–1) and place values 1, 2, 4, 8, …; base 16 uses sixteen — 0–9 then A–F for ten through fifteen — and place values 1, 16, 256, …. Nothing about the underlying quantity changes when you switch base; you have only changed the alphabet and the size of each step.

Why binary: two states are all the hardware can promise

Computers count in base 2 for a physical, not mathematical, reason. Inside a chip a bit is stored as a voltage, a charge, or a magnetic direction — and the one thing hardware can distinguish reliably, cheaply, and billions of times per second is the difference between two clearly separated states: high or low, charged or empty, on or off. A ten-state circuit that had to tell 0.3 volts from 0.4 volts would be slow and error-prone; a two-state circuit only has to tell "clearly high" from "clearly low," which tolerates noise and manufacturing slop. So every number, letter, pixel and instruction ultimately lives as a row of two-valued bits, and base 2 is the natural way to write them. The cost is length: 156 needs three decimal digits but eight binary ones, because each binary place only doubles rather than multiplying by ten.

Why hex: a compact window onto the bits

Long binary strings are correct but miserable to read — miscount the zeros in 10011100 and you have the wrong number. Hexadecimal fixes this with one lucky fact: 16 is 2⁴, so exactly one hex digit encodes exactly four bits, with no remainder and no arithmetic. Four bits (a "nibble") range from 0000 to 1111, which is 0 to 15 — precisely the range of one hex digit, 0 to F. Group a byte's eight bits into two nibbles and each becomes one hex digit, so two hex digits are always exactly one byte:

1001 1100 (one byte, eight bits) └─┬─┘└─┬─┘ 9 C each nibble → one hex digit = 9C two hex digits = one byte

This is why hex is everywhere a programmer looks at raw memory. A byte is unambiguously two characters; a 4-byte word is eight; a 16-byte hash is 32 hex characters — always predictable, always aligned. Octal (base 8 = 2³) does the same trick with three bits per digit, which is why it survives mainly in Unix file permissions: chmod 755 is three octal digits, one per rwx group, where 7 = 111 = read+write+execute. But a byte is eight bits, which splits evenly into two nibbles, not into threes — so hex, aligned to byte boundaries, won and octal faded.

Worked example: 156 across all four bases

Let's convert one concrete value, decimal 156, into binary, octal and hex by hand, so the place-value math is visible. To go from decimal into any base you repeatedly divide by that base and read the remainders from bottom to top. Here is 156 into binary (divide by 2):

156 → binary (divide by 2, read remainders bottom-up) 156 ÷ 2 = 78 r 0 78 ÷ 2 = 39 r 0 39 ÷ 2 = 19 r 1 19 ÷ 2 = 9 r 1 9 ÷ 2 = 4 r 1 4 ÷ 2 = 2 r 0 2 ÷ 2 = 1 r 0 1 ÷ 2 = 0 r 1 read up: 1001 1100

Check it by adding the place values of the 1-bits: 128 + 16 + 8 + 4 = 156. Now the same value into hex (divide by 16), and into octal (divide by 8):

156 → hex 156 → octal 156 ÷ 16 = 9 r 12 156 ÷ 8 = 19 r 4 9 ÷ 16 = 0 r 9 19 ÷ 8 = 2 r 3 12 is C, so: 2 ÷ 8 = 0 r 2 read up: 9C read up: 234

And the shortcut the "one digit = four bits" rule promises: split the binary 1001 1100 straight into nibbles 1001=9 and 1100=C to read 9C with no division at all — and into triplets 010 011 100 = 234 for octal. Four faces, one number:

156 (dec) = 0b1001_1100 (bin) = 0o234 (oct) = 0x9C (hex)

The 0b, 0o and 0x prefixes are how most programming languages label a literal's base in source code, so the reader (and the compiler) knows that 0x9C means 156 and not something in decimal.

Hex in the wild: colors and byte dumps

Once you see bytes as hex pairs, two everyday formats click into place. A CSS color like #9C27B0 is simply three bytes — red, green, blue — each written as two hex digits, i.e. each on the 0–255 scale. Read #9C27B0 as red 9C = 156, green 27 = 39, blue B0 = 176: a strong purple. That is all a hex color is — #RRGGBB is three base-16 bytes glued together after a hash.

#9C27B0 rgb 156,39,176

The second place is a hex dump, the standard way to inspect a binary file. Each row shows an offset, then sixteen bytes as hex pairs, then the same bytes as printable characters. Because every byte is a fixed two characters, columns line up perfectly and you can eyeball structure — here the ASCII bytes of the word Polyatic:

Offset Hex bytes ASCII 00000000 50 6f 6c 79 61 74 69 63 Polyatic │ │ │ P (0x50=80) … c (0x63=99)

Each byte 50 6f 6c … is one character's code: 0x50 = 80 = P in ASCII, 0x63 = 99 = c. The whole discipline of reading memory, network packets, and file formats leans on this hex-per-byte view.

Negative numbers: two's-complement in plain terms

So far every number has been non-negative. But a fixed-width byte has no separate slot for a minus sign — it is just eight bits. Computers solve this with two's-complement, a scheme where the top bit carries a negative weight. In an 8-bit byte the place values are the usual 128 64 32 16 8 4 2 1, except the leftmost 128 becomes −128. So 1111 1010 is read as −128 + 64 + 32 + 16 + 8 + 2 = −6. The recipe to write a negative is: take the positive, flip every bit, then add 1.

−6 as an 8-bit two's-complement byte +6 = 0000 0110 invert = 1111 1001 (one's complement) add 1 = 1111 1010 = 0xFA = 250 read as unsigned

Two things make this scheme win over a simple sign bit. First, ordinary addition "just works": add the bits of −6 and +6 and they overflow to 0000 0000, exactly 0, with no special-case circuitry. Second, there is only one zero, instead of a wasteful +0 and −0. The catch — and it is a real one — is that the bits alone never tell you whether they are signed. The pattern 1111 1010 is 250 if the byte is unsigned and −6 if it is signed; identical bits, two meanings. You only know which from the type: the declared width (8, 16, 32, 64 bit) and whether it is signed. That is why a bare number-base converter shows negatives as a plain −9C (signed-magnitude) rather than guessing a width you never told it.

The one honest caveat

Everything here is about integers. Fractions in other bases work by extending place values to the right of the point — in binary those places are ½, ¼, ⅛, … — but many decimal fractions have no exact binary form. 0.1 in decimal is a repeating binary fraction, the root cause of the classic 0.1 + 0.2 = 0.30000000000000004 surprise in floating-point code. Base conversion of whole numbers is exact and reversible; base conversion of fractions can round. When you need a specific integer's four faces without dividing by hand, the converter below does it live and BigInt-safe, so even a 256-bit value keeps every digit.

Convert any number between bases, live

Type a value and watch its binary, octal, decimal, hex and any base 2–36 forms update instantly — with a bit-length readout and digit grouping. Runs entirely in your browser; nothing is uploaded.

Try the number base converter →

Open the number base converter → to convert between binary, octal, decimal and hex live, or browse all Polyatic tools.