Answers · Where the data lives
Why do programmers write numbers in hexadecimal instead of just normal numbers?
In shortProgrammers use hexadecimal because each digit stands for exactly four bits, so one byte is always written as two digits and the columns line up.
Hexadecimal is used because it is binary written in a shorter alphabet: one hexadecimal digit is exactly four bits, so every byte is exactly two digits. That fixed width, not the base itself, is the point. The hardware stores switches; the notation is chosen so a person can see those switches in groups without counting long strings of ones and zeros.
The columns are worth powers of sixteen
The familiar decimal system is place value: 407 means 4×100 + 0×10 + 7×1. Change the multiplier between columns and the same rule holds. In base sixteen the columns are worth 1, 16, 256 and 4096, and each column holds 0 to 15 of its value. The digits 0 to 9 keep their usual meaning; 10 to 15 are written A to F so that every digit stays one character wide. This is typography, not mathematics.
Take the value 206. Divide by 16: 16×12 = 192, and 206 − 192 = 14, so the first digit is 12 and the second is 14. Written with letters for ten to fifteen, 12 is C and 14 is E, which gives CE. Check the place value: 12×16 + 14 = 192 + 14 = 206. The largest byte is 255, and 255 = 15×16 + 15 = 240 + 15, so it is FF. The next value, 256, is 1×256 + 0×16 + 0 = 100 in base sixteen, three digits, because it no longer fits in eight bits.
Four switches per character
Sixteen is not chosen for looks. 16 = 2^4, so four binary switches have exactly sixteen patterns, from 0000 to 1111, and each pattern maps to one hexadecimal digit. Eight switches, one byte, split into two groups of four with nothing left over. The value 206 is 11001110 in binary: 128 + 64 + 8 + 4 + 2 = 206. Split it as 1100 and 1110. The first group is 12, written C; the second is 14, written E. So the byte is CE. That is two lookups, not a division.
| four bits | value | hex digit |
|---|---|---|
| 0000 | 0 | 0 |
| 0001 | 1 | 1 |
| 0010 | 2 | 2 |
| 0011 | 3 | 3 |
| 0100 | 4 | 4 |
| 0101 | 5 | 5 |
| 0110 | 6 | 6 |
| 0111 | 7 | 7 |
A dump makes the fixed width visible. A line that begins at offset 0x10 begins at 1×16 + 0 = 16 bytes into the file, and if each line holds sixteen bytes the next offset is 0x20, which is 2×16 + 0 = 32. The offset column itself uses the same two digit width, so the eye can count columns and see where a record starts. That is impossible when the byte column has one line showing 7, the next 42 and the next 255 and the one after 256: the digits shift even though the hardware has crossed a boundary.
The natural guess is that hexadecimal is an arbitrary notation, perhaps favoured by programmers for compactness alone. Compactness is real, but decimal is more compact, and decimal still fails. A byte can be any value from 0 to 255, so in decimal it occupies one, two or three characters: 7, 42, 255. A dump would have wandering columns, and byte boundaries would fall in different places on different lines. Worse, 255 and 256 are both three decimal digits, yet 255 is the largest value eight bits can hold and 256 needs a ninth bit. Decimal hides the exact place where the hardware changes width. Hexadecimal does not: two digits cover 00 to FF, and the next value is 100, visibly wider.
Where hexadecimal stops helping
Hexadecimal is a display convention, not a storage format. The file contains bits; a hex dump is a rendering of those bits. The 0x in front of a value is only a marker saying that the following digits are in base sixteen, so 0x2A is 2×16 + 10 = 32 + 10 = 42 in decimal. Hexadecimal is also poor for quantities meant to be read as quantities: money, counts and measurements are decimal, and converting them to hex makes them harder, not easier. If a file stores the characters 4 and 2, a dump shows 34 32, which are the hexadecimal codes for those characters: 34 is 3×16 + 4 = 52, and 32 is 3×16 + 2 = 50. That distinction matters more than the base itself.
Worth keeping
- One hexadecimal digit covers four bits, so two hexadecimal digits cover exactly one eight bit byte.
- Decimal cannot keep byte columns aligned because a byte takes one, two or three decimal digits.
- Hexadecimal is a rendering of binary, not a different kind of storage.
Where this goes next
This question is one step of Bytes, Text and File Formats, a course that works through its subject from the beginning. The page that teaches this part of it is Counting in Twos and Sixteens, and it reads the whole way through whether or not you have an account.
What an account changes is the practice: problems standing under every idea, marked with a reason rather than a tick, in a copy of the course that is yours.
Read that pageThe whole courseOther questions people ask about this
- Why is the date off by one day after I save it and read it back?You saved a calendar date as if it were a precise moment, so showing it in another time zone can move it to the day before or after.
- Is UTF-8 the same thing as Unicode, or what's the difference?No: Unicode assigns a number to each character, while UTF-8 is a way of writing those numbers as bytes, using one to four bytes per character.
- Why does my CSV file fall apart when one of the fields has a comma in it?Because the same comma both separates fields and sits inside the value, so a reader splitting on commas finds one field too many and every later column shifts.
- Why doesn't 0.1 plus 0.2 come out as 0.3?A tenth cannot be stored exactly in binary, so the two stored values are already slightly wrong before they are added.
- How do I make a step safe to run again when I'm not sure whether it already ran?Change the operation so that sending it twice leaves the same stored data as sending it once, instead of relying on remembering not to send it again.
- Why does my count come out doubled after the job restarts?Your count doubled because the restart replayed events the saved counter already held: the state and input position were not saved as one snapshot.