Skip to content
academia.sh

Lesson 02 / 16

Hexadecimal and Octal Bases

Hexadecimal and octal representations, which give compact notation without breaking bit boundaries, and how to convert between them.

Contents

The previous lesson established that binary representation matches the hardware exactly. That correspondence costs length: a 32-bit value is a thirty-two-symbol sequence such as 01000001010000100100001101000100, and reading, writing, or comparing this sequence without error is inconvenient for a person.

This lesson asks the following question: how can the same value be written with fewer symbols without hiding the bit structure? The answer lies in restricting the base choice to a power of two.

Why Powers of Two

If the base is chosen as 2k2^k, every digit in that base corresponds to exactly kk bits. This reduces conversion from computation to grouping: it suffices to split the bits into blocks of kk and replace each block with a single digit.

This does not hold for the decimal system, since 1010 is not a power of two. Looking at the decimal digits of 4703 says nothing about its bit pattern; conversion requires division. Hexadecimal and octal systems are therefore preferred: they give compactness while doing so without breaking bit boundaries.

Hexadecimal System

In the hexadecimal system the base is 1616. Sixteen digits are required; since the ten symbols of the decimal system are insufficient, the letters AF are used for the remaining six values:

Hex Decimal Binary Hex Decimal Binary
0 0 0000 8 8 1000
1 1 0001 9 9 1001
2 2 0010 A 10 1010
3 3 0011 B 11 1011
4 4 0100 C 12 1100
5 5 0101 D 13 1101
6 6 0110 E 14 1110
7 7 0111 F 15 1111

Since 16=2416 = 2^4, every hexadecimal digit carries exactly four bits. A group of four bits is called a nibble. A byte is eight bits, that is, two nibbles; every byte is therefore written with exactly two hexadecimal digits. This is the real reason for the widespread use of hexadecimal notation: byte boundaries remain visible in the representation.

Whether the letters are written in upper or lower case does not change the value; 0xFF and 0xff denote the same value. Within a single text, one form is chosen for consistency.

Converting by Grouping

To go from binary to hexadecimal, the bits are grouped in fours starting from the right, and each group is replaced by its counterpart in the table. If the leftmost group has fewer than four bits, it is padded with leading zeros.

01004 00011 01004 00102 01004 00113 01004 01004\underbrace{0100}_{4}\ \underbrace{0001}_{1}\ \underbrace{0100}_{4}\ \underbrace{0010}_{2}\ \underbrace{0100}_{4}\ \underbrace{0011}_{3}\ \underbrace{0100}_{4}\ \underbrace{0100}_{4}

A thirty-two-bit sequence collapses to eight hexadecimal digits: 41424344. The reverse direction applies the same rule in the opposite order: each hexadecimal digit is replaced by four bits.

Grouping from the right matters. Grouping from the left, when the bit count is not a multiple of four, shifts every positional value and produces an incorrect result.

The Course’s Shared Example

The value obtained above is the example this course will return to again and again:

0x41424344\texttt{0x41424344}

Its decimal counterpart can be computed from the positional values:

4×167+1×166+4×165+2×164+4×163+3×162+4×161+4×160=1,094,861,6364 \times 16^7 + 1 \times 16^6 + 4 \times 16^5 + 2 \times 16^4 + 4 \times 16^3 + 3 \times 16^2 + 4 \times 16^1 + 4 \times 16^0 = 1{,}094{,}861{,}636

This is the interpretation of the 32-bit pattern as an unsigned integer. The same pattern will be interpreted differently in later lessons: as a signed integer, as a real number, as a four-letter piece of text, and as a sequence that looks different depending on the byte order in memory. The bit pattern stays the same across all these readings; what changes is only the interpretation rule applied to it.

This distinction carries the central idea of the lesson about the nature of data: memory contains no such thing as a “number” or “text” — it contains bit patterns and the interpretations applied to them.

Octal System

In the octal system the base is 88; the digits range from 00 to 77. Since 8=238 = 2^3, every octal digit carries three bits, and conversion again proceeds by grouping — this time in threes.

1117 1015 1015=7558\underbrace{111}_{7}\ \underbrace{101}_{5}\ \underbrace{101}_{5} = 755_8

Because groups of three do not align with byte boundaries, octal representation lags behind hexadecimal for general-purpose use: 8 bits, grouped in threes, yield two full digits and two leftover bits.

Octal survives in domains whose natural unit is three bits. File permissions on Unix-derived systems are the standard example: read, write, and execute permissions form a group of three bits, and this group is repeated separately for owner, group, and others. The notation 755 encodes the permission rwxr-xr-x; the first digit 7=11127 = 111_2 states that all three permissions are granted, and the other two digits 5=10125 = 101_2 state that write permission is off. The permission model is treated in detail in the Linux curriculum.

Notational Conventions

Which base a number is written in must be stated in code just as it is in text. Common prefixes:

Prefix Base Example Value
0b 2 0b1011 11
0o 8 0o755 493
0x 16 0x1F 31
(none) 10 31 31

Prefixes are not part of the value; they only state which base to read. When the same value is written in four different forms, the same bit pattern results in memory.

The following program shows the conversions and formatting:

value = 0x41424344

print(value)                      # 1094861636
print(hex(value))                 # 0x41424344
print(bin(value))                 # 0b1000001010000100100001101000100
print(f"{value:#010x}")           # 0x41424344  (at least 8 digits, prefixed)
print(f"{value:032b}")            # 01000001010000100100001101000100

# Reading byte by byte: each byte is two hexadecimal digits.
for shift in (24, 16, 8, 0):
    byte = (value >> shift) & 0xFF
    print(f"{byte:#04x}", byte)   # 0x41 65 / 0x42 66 / 0x43 67 / 0x44 68

It is worth noting that the bin output has 31 digits rather than 32: the built-in function does not print leading zeros. When fixed-width representation is required, a format specifier (032b, #010x) is used. Fixed width is needed to keep columns aligned when memory contents are inspected.

The >> and & operators in the loop extract a specific byte from the pattern; these operators will be defined in the lesson on bit-level operations.

Summary

  • When the base is chosen as 2k2^k, conversion between bases is not computation but grouping in blocks of kk.
  • In the hexadecimal system every digit carries four bits, so every byte is exactly two digits; byte boundaries remain visible in the representation.
  • Converting binary to hexadecimal groups from the right; a leftover left group is padded with zeros.
  • In the octal system every digit is three bits; because it does not align with byte boundaries, its use is limited to naturally three-bit fields such as file permissions.
  • The 0b, 0o, 0x prefixes state the base to be read, not the value.
  • The course’s shared example, 0x41424344, has the value 1,094,861,6361{,}094{,}861{,}636 under an unsigned interpretation.

Next Step

Up to this point every number has been treated as non-negative. Hardware, however, has no separate place to hold a minus sign; the sign, too, must be encoded within the bit pattern. The next lesson takes up how this is done, and why the chosen method leaves the addition circuit unchanged.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close