Chapter 07

Data Representation

Middle School
At a glance
Core ideaThe same bits mean different things under different encodings.
Key termTwo's complement — negate a number by inverting its bits and adding 1.
You can…Convert between binary, hexadecimal and decimal by hand.
Watch outFloats are approximate — 0.1 + 0.2 is not exactly 0.3.
Theory

A positional number system in base b gives digit d at position i the weight d · bⁱ. Binary is base 2, so 1011₂ = 1·8 + 0·4 + 1·2 + 1·1 = 11₁₀. Eight bits form a byte, representing 0–255. Because long binary strings are error-prone for humans, we group them in fours as hexadecimal (base 16, digits 0–9 and A–F): one hex digit = exactly four bits.

To represent negative integers, modern hardware uses two's complement: negate a number by inverting all bits and adding 1. In n bits this represents the range −2ⁿ⁻¹ … 2ⁿ⁻¹−1, and — crucially — the same adder circuit from Chapter 1 handles both signs with no modification.

Real numbers use floating point (IEEE 754): a value is stored as sign × mantissa × 2^exponent, like scientific notation in binary. This trades exactness for enormous range. Text is mapped to integers by an encoding — ASCII (7 bits) or Unicode/UTF-8 (variable width) — and colour is typically three bytes: red, green, blue.

8 bits
= 1 byte
2⁸ = 256
values in one byte (0–255)
4 bits
= 1 hex digit (a nibble)
2¹⁰ = 1024
bytes in one kibibyte
2³² ≈ 4.3 bn
range of a 32-bit number
3 bytes
one RGB colour (24-bit)
Explanation

Two's complement feels like a trick until you see why it works: it is arithmetic modulo 2ⁿ. On a wheel of 2ⁿ positions, subtracting is the same as adding the complement and letting the top carry fall off the edge. That is why one circuit adds and subtracts, positive and negative alike — the elegance that made it universal.

Floating point explains a famous surprise: 0.1 + 0.2 ≠ 0.3 exactly. One-tenth has no finite binary expansion, just as one-third has no finite decimal expansion, so it is stored slightly rounded. The result is right to about sixteen significant digits — never confuse "computer arithmetic" with "exact mathematics."

Key distinction. Bits carry no inherent meaning. An encoding is a promise about how to read them. Reading UTF-8 bytes as a JPEG, or an integer as a float, is not an error the hardware detects — it is simply a broken promise.

Practical

We will convert between bases by hand-coded logic, then compute a two's-complement negation and confirm that 5 + (−5) = 0 in 8 bits.

# --- Base conversion: decimal integer -> binary string ---
def to_binary(n, width=8):
    if n == 0:
        return "0" * width
    bits = ""
    while n > 0:
        bits = str(n % 2) + bits          # prepend remainder (LSB found first)
        n = n // 2
    return bits.rjust(width, "0")

def from_binary(bits):
    value = 0
    for ch in bits:                       # process most-significant bit first
        value = value * 2 + int(ch)       # Horner's method
    return value

# --- Two's complement negation in a fixed width ---
def negate(bits):
    inverted = "".join("1" if c == "0" else "0" for c in bits)   # invert
    return to_binary(from_binary(inverted) + 1, len(bits))       # add 1

def add8(a_bits, b_bits):
    total = (from_binary(a_bits) + from_binary(b_bits)) % 256     # mod 2^8
    return to_binary(total, 8)

five      = to_binary(5)          # 00000101
neg_five  = negate(five)          # 11111011  (== 251 unsigned, == -5 signed)
print(five, neg_five)
print(add8(five, neg_five))       # -> 00000000 : the carry falls off the top

# Text: a character is just its code point
print(ord('A'), to_binary(ord('A')))   # 65  01000001

Notice the loop in from_binary is Horner's method — multiply-by-base then add — the same technique used to evaluate polynomials efficiently. The mod-256 in add8 is the wheel: the overflow carry simply vanishes, giving zero.

Q&A
Q1 Why does one hexadecimal digit equal exactly four bits?

Because 16 = 2⁴. Each hex digit ranges over 16 values, which is precisely the number of patterns four bits can make. So 0xF3 is 1111 0011 — you can convert nibble by nibble with no arithmetic, which is why hex is the programmer's shorthand for raw memory.

Q2 In 8-bit two's complement, what does 10000000 represent?

−128, the most negative value. Its magnitude has no positive counterpart in the range (which stops at +127), so 10000000 is its own negation on paper — a classic overflow edge case that careful code must guard against.

Q3 Why can't floating point store 0.1 exactly?

Because 0.1 = 1/10, and 10 has the prime factor 5, which is not a power of 2. In binary its expansion repeats forever (0.0001100110011…), so it must be rounded to fit the finite mantissa. Values that are sums of powers of two — like 0.5 or 0.75 — are stored exactly.

Q4 What is the difference between ASCII and UTF-8?

ASCII assigns 128 characters to single 7-bit codes — enough for English but nothing else. UTF-8 is a variable-length encoding of Unicode's ~150,000 characters: it uses one byte for the ASCII range (so it is backward compatible) and two to four bytes for everything else, from "é" to "字" to "😀".

Concept mind map

How the ideas connect

Every key idea in this chapter, branching from the core concept — use it to see the whole picture at a glance.

base-2 binarybase-16 hextwos complementASCIIbits and bytessame bits, meaningData representation
Infographic

The key facts, visualised

8 bits
Equal 1 byte; one byte holds 256 different values (0 to 255).
Hex
Base-16 using 0-9 and A-F; one hex digit equals 4 bits.
Two's complement
Negate a number by inverting all bits and adding 1.
ASCII
A code mapping characters to numbers, e.g. A is 65.
Solved examples

Worked problems, step by step

Follow each solution line by line, then try to reproduce it on paper before moving on.

Example 1Convert decimal 13 to 8-bit binary.

  1. 13 = 8 + 4 + 1.
  2. Set the 8, 4 and 1 place bits.

Example 2Convert binary 1111 to hexadecimal and decimal.

  1. 1111 = 8 + 4 + 2 + 1 = 15.
  2. 15 in hex is F.
Practice problem set

Now you try

Work each one out first, then tap to reveal the worked answer.

1How many bits are in a byte, and how many values does it hold?
A byte is 8 bits and can represent 256 values, 0 through 255.
2Convert decimal 10 to binary.
10 = 8 + 2, so 1010.
3Why use hexadecimal?
One hex digit maps to exactly 4 bits, so hex writes long binary strings compactly and readably.
4How does two's complement store -5 in 8 bits?
Take 5 (00000101), invert to 11111010, add 1 to get 11111011.
5What is ASCII?
A standard code that assigns each character a number, for example capital A is 65.
6Convert hex A to decimal.
Hex A equals decimal 10.