Converting Between Bases Without a Calculator
In the last article we met binary, octal and hex. In this one we practise converting between them, because you will do it constantly. A colleague leaves 0x2D in a code review. A register dump from a crashed microcontroller shows 0xFF3C. The simulator prints 8'b10110100. Each time, you want to know what the value is without breaking your train of thought to open a calculator. With a little practice these conversions become something you see rather than compute.
Decimal to binary
There are two methods: the systematic one for paper, and the quick one for your head.
Repeated division by 2
Divide by 2 repeatedly and write down the remainders. Read them from the last to the first. Converting 45:
| Step | Division | Quotient | Remainder |
|---|---|---|---|
| 1 | 45 ÷ 2 | 22 | 1 (LSB) |
| 2 | 22 ÷ 2 | 11 | 0 |
| 3 | 11 ÷ 2 | 5 | 1 |
| 4 | 5 ÷ 2 | 2 | 1 |
| 5 | 2 ÷ 2 | 1 | 0 |
| 6 | 1 ÷ 2 | 0 | 1 (MSB) |
Reading upwards from the bottom, 45 is 101101. The first remainder is the least significant bit (LSB), and it tells you straight away whether the number is odd.
Subtracting powers of two
For numbers up to a few hundred, most engineers do it by finding the largest power of two that fits, subtracting it, and repeating.
Take 45 again. The largest power of two in 45 is 32, so bit 5 is 1. $45 - 32 = 13$. The largest power in 13 is 8, so bit 3 is 1. $13 - 8 = 5$. Then 4, so bit 2 is 1. $5 - 4 = 1$, so bit 0 is 1. Bits 5, 3, 2 and 0 are set: 101101. Once the powers of two are in your head this is faster than dividing.
Decimal to hexadecimal
Same division method, dividing by 16, with remainders 10 to 15 written as A to F. Converting 1274:
- $1274 \div 16 = 79$ remainder 10, which is A
- $79 \div 16 = 4$ remainder 15, which is F
- $4 \div 16 = 0$ remainder 4
Reading upwards, 1274 is 0x4FA.
Binary to hexadecimal
This is the conversion you will do most often, and it needs no arithmetic. Because $16 = 2^4$, every four bits are one hex digit. Group the bits in fours starting from the right and look each group up.
Converting 101101110010:
1011 0111 0010
B 7 2
The answer is 0xB72. If the leftmost group is short, pad it with zeros on the left. 1011011100 becomes
0010 1101 1100
2 D C
which is 0x2DC.
Always group from the right. Grouping from the left shifts every weight and gives a wrong answer that looks plausible.
Hexadecimal to binary
Go the other way by expanding each digit to four bits. 0xCAFE:
| Hex | C | A | F | E |
|---|---|---|---|---|
| Binary | 1100 | 1010 | 1111 | 1110 |
So 0xCAFE is 1100101011111110. When you are reading a status register this is the skill that matters: seeing 0xDE and immediately knowing it is 1101 1110 tells you which flag bits are set.
Binary to octal
The same grouping trick with three bits per digit, since $8 = 2^3$. Converting 10110111:
010 110 111
2 6 7
which is 0o267.
Hexadecimal to decimal
Use the column weights 4096, 256, 16 and 1. Converting 0x1A3C:
$$1 \times 4096 + 10 \times 256 + 3 \times 16 + 12 \times 1 = 4096 + 2560 + 48 + 12 = 6716$$
For a rough figure while debugging, 0x1000 is about 4000 and 0xA00 is about 2500, so 0x1A3C is a bit under 7000. Often that is all you need.
Summary of methods
| From | To | Method |
|---|---|---|
| Decimal | Binary | Repeated ÷ 2, read remainders from the bottom |
| Decimal | Hex | Repeated ÷ 16, read remainders from the bottom |
| Binary | Decimal | Sum of bit × $2^i$ |
| Binary | Hex | Group in fours from the right, look up |
| Binary | Octal | Group in threes from the right, look up |
| Hex | Binary | Expand each digit to four bits |
| Octal | Binary | Expand each digit to three bits |
| Hex | Decimal | Sum of digit × $16^i$ |
Practice Exercise
Convert 200 to binary and to hex without a calculator.
Solution
By repeated division: 200 → 100 r0, 100 → 50 r0, 50 → 25 r0, 25 → 12 r1, 12 → 6 r0, 6 → 3 r0, 3 → 1 r1, 1 → 0 r1. Reading upwards, 200 is 11001000.
By subtracting powers of two: 128 fits (bit 7), leaving 72; 64 fits (bit 6), leaving 8; 8 fits (bit 3). Bits 7, 6 and 3: 11001000. Same answer.
Group in fours: 1100 1000 is 0xC8.
So far every number has been positive. In the next article we look at how hardware represents negative numbers when all it has is voltages, and why two’s complement was the scheme that won.