Writing large binary patterns, e.g. of 32 bits, is pretty tedious. Let's look at a condensed way to represent binary patterns, using base 8 or base 16.
We'll be doing conversions between three different numerical bases: bases 2, 8, and 16. But before we do, it's useful to consider something we instinctively do with base 10 numbers that relates strongly to how we'll do those other base conversions.
While we think that we count in base 10, in a certain sense we actually think in base 1000. To see what I mean, try reading aloud the following number: 123,456,789. You probably said “123 million, 456 thousand, 789”. We even add commas (or periods in some nations) to separate the digits into groups of 3.
Importantly, you don't announce each place value when you said the number, or even when you think about it. No one says (or thinks) “4 hundred-thousands, 5 ten-thousands, and 6 thousands”. Instead, we break the number at what we might call benchmark place values, such as the thousands place, and we think of the digits to the left of each benchmark place value relative to that place value. So, we see the 4 and the 5 in our sample value as being in the 100's and 10's place, relative to 1000, and the digits 456 collectively tell us how many thousands we have.
If you think about it, we're actually describing our decimal number in base 1000. What would be the place values in base 1000? They'd be powers of 1000. We'd have a 1's place, and then a thousand's place, a million's place, etc. These are exactly the benchmark place values we use to break up a decimal number. Indeed, we have terms in English, and in many languages, for these benchmark place values (thousand, million, billion, trillion), but not for most other place values.
What this shows is that you can convert between certain bases by choosing benchmark place values in the smaller base, and grouping digits around those place values, to arrive at the larger base. For this to work, the larger base has to be some power of the smaller one. 1000 is 10^3, for instance, and we convert from base 10 to base 1000 by grouping digits by 3's.
As long as the larger base is some integer power of the smaller one, the system will work. If we chose to, we might have grouped decimal digits by 4's, made up words for each 4 powers of 10, and counted in base 10000. (Indeed, one Chinese numbering system does exactly this.).
So, how does this apply to binary values? Consider the 9-bit binary value 101011110. Let's write it with commas grouping every three bits, just like we do for decimal values: 101,011,110.
Answer 1
So, we can view this 9-bit binary number as a 3-digit base-8 (or octal) number, in a fashion very much like what we did for base 10 and base 1000. For instance, bit 4 can be viewed as the 16's place, or as the 2 8's place, etc. The diagram shows this interpretation for all 9 bits.
And, we can use a single digit for each grouping (something we can't do in the base 1000 case) since the highest value we can count with three bits is 111, or 7. So, our sample binary value translates to base 8 thus:
101,011,1102 => 101 64's, 011 8s, 110 1s => 536
So, why do we care? As discussed earlier, sometimes you want to put a particular pattern of bits into an integer. The numerical value is incidental, but you have to assign some number into an int in order to get the bit pattern. Assigning an octal number is more convenient than assigning a decimal, because once you get the hang of it, it's very easy to translate a desired bit pattern into an octal value and vice versa.
Most programming languages, including ARM assembly, let you express a constant in octal for this reason. You do this by adding an otherwise meaningless leading 0. 042, for instance, is octal 42, or decimal 34.
We introduced octal first because of its natural connection with the 3-digit groupings in base 10, and because octal values use the familiar digits 0-7. But octal has two problems as a base. First, it compresses only 3 bits into each octal digit. Second, integers come in sizes of 8, 16, 32, and perhaps 64 bits. None of those sizes are divisible by 3, so the most significant octal digit is always encoding only 1 or 2 bits – somewhat inelegant.
What would be better, and what is more common in assembly, is to use 4-bit groups, breaking the binary number up thus:
0111,1110,0111,0001,0101,1100,0001,1100
So, we can also convert to base 16, or hexadecimal (often abbreviated to "hex"), by reading each 4-bit group as a base-16 number. The diagram shows how we're interpreting the bits. Bits 8-11, for instance, are interpreted relative to the 256's place, and show how many 1's, 2's, 4's, and 8's of 256's there are. In the diagram example, that's 8+4 = 12 256's.
Grouping by 4 bits crams more bits into each digit in the larger base, and 4 divides evenly into every integer bit-size in common use. This is good. What is bad, however, is that we have to count up to 15 (the highest possible value in one place in hexadecimal) using single symbols. We only had to count up to 7 per place value in octal.
The decimal system we're used to only supplies digits up to 9, so the universal convention is to use letters A-F for the values 10-15. Hexadecimal values are combinations of the standard digits and these added “digits”.
So, the binary value from the diagram translates into 7E715C1C.
To convert 4-bit groups to hexadecimal, you need to memorize all 16 4-bit patterns and their corresponding hexadecimal digit – rather more tedious than the 8 3-bit patterns of octal, especially since it is our instinct not to think of values higher than 9 as single symbols. The table in the diagram, and repeated here, will help. Consider these to be vocabulary:
Hex Digits:
0 0000 8 1000
1 0001 9 1001
2 0010 A 1010
3 0011 B 1011
4 0100 C 1100
5 0101 D 1101
6 0110 E 1110
7 0111 F 1111
00111010100110111110110001111101
11011110101011011011111011101111
As with octal, ARM assembly lets you write an integer constant in hexadecimal. You do this by preceding it with a #0x, e.g. #0xDEADBEEF, as we'll see in our example programs.
In modern code, prefer hexadecimal. Octal is more common in code from the 1970s, and you'll find it still crops up now and then, especially in systems designed at that time, e.g. in the representation of file permissions in Unix.
Note that each byte in a binary pattern translates to two hex digits. It's remarkably easy to slide into thinking each hex digit represents a byte, and to write, e.g. a four byte binary pattern using only 4 hex digits. Since each hex digit represents just four bits, you'd need 8 hex digits, not four for a four byte value. (Four bits, by the way, are a nybble -- a whimisical term for a "small byte". It may sound silly, but it's the accepted term.)