Data representation and character encoding

← Index · Solutions

Priority key · Priorities guide emphasis; they do not remove taught scope.

Exam recall

Binary ↔ hex: group four bits from the right. Preserve required width. A Unicode code point is not its UTF-8 byte sequence.

Bits, bases and ranges priority/medium

The digits are not the value by themselves: their positions and base determine the value. 101 in binary is five; 101 in denary is one hundred and one. Changing representation preserves the quantity, just as writing “five” instead of “5” does.

A bit is 0 or 1; a nibble is 4 bits; a byte is 8 bits. In a positional system, a digit’s contribution is its value multiplied by the base raised to its position, starting at position 0 on the right. Binary uses base 2; denary base 10; hexadecimal base 16 with A–F representing 10–15.

An unsigned n-bit integer has 2ⁿ possible patterns and values 0 to 2ⁿ−1. Four bits represent 0–15, not 1–16. State the unsigned assumption when the question does not specify a signed representation. This pack follows the supplied representation chapter rather than adding unrelated numeric formats.

Conversion procedures priority/high

Binary 1101:
Position       3   2   1   0
Place value    8   4   2   1
Digit          1   1   0   1
Contribution   8 + 4 + 0 + 1 = 13 denary

Repeated division works in the opposite direction: dividing by the base removes the rightmost place and leaves that digit as the remainder. That is why remainders are produced right-to-left. The first remainder is the least significant digit, not the first digit to write.

ConversionReliable procedure
Binary → denaryAdd the weights of positions containing 1.
Denary integer → binaryRepeatedly divide by 2; read remainders from last to first. Handle zero explicitly in an algorithm.
Hex → denaryMultiply each digit value by its power of 16 and sum.
Binary ↔ hexGroup bits into sets of four from the right; pad the left with zeroes if needed. Convert each nibble separately.
Denary integer → hexDivide repeatedly by 16 and map remainders 10–15 to A–F.

Hexadecimal is shorter and easier for humans to read/copy than long binary strings, while each hex digit maps exactly to four bits. It does not change the underlying value or mean the computer stores the number as human-readable hex characters.

Where the bases are used priority/medium

BaseExampleWhy it fits
DecimalDisplay an order quantity of 25Familiar human-facing notation.
BinaryStore the value as a bit patternRepresents data in digital systems.
HexadecimalRGB colour #19A0FF or a memory addressCompact notation: one hex digit maps to four bits.
Hex:        0       A              F       0
            ↓       ↓              ↓       ↓
Binary:   0000    1010           1111    0000
Result:   00001010               11110000

Keep every nibble when the required output specifies four bits per input digit.

Characters and encodings priority/high

A character set assigns numeric codes to characters. An encoding specifies how those codes are represented as bytes. ASCII uses 7-bit codes for 128 characters. An ASCII character may be stored in an 8-bit byte with a leading zero. Unicode provides a much larger character repertoire across languages and symbols. Unicode is not universally 16 bits per character: UTF-8, for example, uses variable-length encodings. The first 128 Unicode code points match ASCII, and UTF-8 encodes these using the same single-byte values.

ord("A") returns its code point, 65; chr(65) returns "A". A code point’s hexadecimal label such as U+1F631 identifies a number; its numeric conversion is different from asking for its encoded UTF-8 bytes.

Exam mistakes

Keep leading zeroes when a fixed width is requested. Label the base. Do not confuse the numeric value 65 with the two-character string "65".

Worked example — adapted from HCI 2024 Q6

Convert hexadecimal DAB to denary and binary.

Denary: 13×16² + 10×16 + 11 = 3499. Binary: D→1101, A→1010, B→1011, so 110110101011₂. Converting through nibble groups preserves every position and avoids repeated division.

Writing the conversion algorithm priority/high

When asked for the algorithm, bin() or hex() may bypass the assessed skill. Repeated division generates the least significant digit first, so prepend it (or reverse the collected digits at the end):

def denary_to_hex(number):
    # Contract: number is a nonnegative integer.
    digits = "0123456789ABCDEF"
    if number == 0:  # Without this case, the loop would return empty text.
        return "0"
    result = ""
    while number > 0:
        result = digits[number % 16] + result  # Prepend the next rightmost digit.
        number //= 16                         # Remove that base-16 place.
    return result
 
def binary_text(number):
    # Recursive version; contract: number is a nonnegative integer.
    if number < 2:
        return str(number)
    return binary_text(number // 2) + str(number % 2)  # Higher places return first.

For 12, binary_text waits for binary_text(6), then (3), then (1); concatenating returned digits gives "1100". This is text representing twelve in binary, not the denary integer one thousand one hundred.

Practice

Exam focus: HCI 2023 Q4, HCI 2024 Q6 and ASRJC 2025 Q5 use conversions/encoding. In a calculation, show place-value products or nibble groups; in an explanation, distinguish the character repertoire from the byte encoding. The exact originals are in HCI 2024 p.4 and ASRJC 2025 p.4.

Answering approach: identify source base → identify target base/width → choose the shortest justified conversion → pad only if required → label the result. Hex-to-binary does not require a detour through denary.

02A — adapted from HCI 2023 Q4. Convert hexadecimal 1C2 to denary and 12-bit binary. Explain one practical advantage of hexadecimal over binary for humans.

450
0001 0110 0002
Hexadecimal is shorter and easier for human to read and copy

02B — adapted from ASRJC 2025 Q5. The ASCII code for M is 77. Write its 8-bit representation. Convert Unicode code point U+1F631 to denary. Explain why Unicode is useful for messaging beyond ASCII, and why “Unicode always needs two bytes” is inaccurate.

(is 8-bit representation binary and 8 slots)
0100 1101
1F631 base 16 = 128561 base 10
Hex is useful as it is shorter and easier for human reading
Why Unicode always needs two bytes?
For a max unicode, FFFFF, it would be 1048575, which is beyond max 8 bit binary can carry which is 255

02C — original, conversion algorithm. Write hex_to_binary(text) for a nonempty uppercase hexadecimal string, returning four bits for every digit. Do not use bin() or int(text, 16). Test "0A" and "F0"; explain why the first result starts with zeroes.

HCI Data Representation Practice Question 2

(a) Decimal numbers to binary numbers

Non-Recursive

# Non-Recursive
def dec_to_bin(number):
    # Special case
    if number == 0:
        return "0"
 
    answer = ""
 
    while number > 0:
        # Remainder becomes the next binary digit
        remainder = str(number % 2)
        # Put the remainder at the front of the answer
        answer = remainder + answer
        # Update number to the quotient so the loop progresses
        number = number // 2
    return answer
 

Recursive

# Recursive
def dec_to_bin(number):
	# base case
	if number < 2:
		return str(number)
	
	return dec_to_bin(number // 2) + str(number % 2)

(b) Decimal numbers to hexadecimal numbers

Non-Recursive

# Non-Recursive
HEX_DIGITS = "0123456789ABCDEF"
 
def dec_to_hex(number):
    if number == 0:
        return "0"
 
    answer = ""
 
    while number > 0:
        # Remainder is an integer from 0 to 15
        remainder = number % 16
 
        # Convert it to the corresponding hexadecimal character
        hex_digit = HEX_DIGITS[remainder]
 
        # Add the digit to the front
        answer = hex_digit + answer
 
        # Continue converting the quotient
        number = number // 16
 
    return answer
 

Recursive

# Recursive
HEX_DIGITS = "0123456789ABCDEF"
 
def dec_to_hex(number):
    # base case
    if number < 16:
        return HEX_DIGITS[number]
 
    # recursive case
    return dec_to_hex(number // 16) + HEX_DIGITS[number % 16]
 
 
# Example
dec_to_hex(2748)
= dec_to_hex(171) + "C"
= dec_to_hex(10) + "B" + "C"
= "A" + "B" + "C"
= "ABC"

(c) Binary numbers to decimal numbers

Non-Recursive

# Non-Recursive
def bin_to_dec(number):
    answer = 0
    no_of_bin = len(number)
 
    for digit in number:
        answer = answer + int(digit) * 2 ** (no_of_bin - 1)
        no_of_bin -= 1
 
    return answer
ChatGPT Ver:
def bin_to_dec(number):
    answer = 0
 
    for i in number:
        answer = answer * 2 + int(i)
 
    return answer

Recursive

# Recursive
def bin_to_dec(number):
	# base case
    if len(number) == 1:
        return int(number)
    
    # recursive case
     return bin_to_dec(number[:-1]) * 2 + int(number[-1])
Trace "1101"
bin_to_dec("1101")
= bin_to_dec("110") × 2 + 1
bin_to_dec("110")
= bin_to_dec("11") × 2 + 0
bin_to_dec("11")
= bin_to_dec("1") × 2 + 1

到达 base case:

bin_to_dec("1") = 1

开始返回:

bin_to_dec("11")
= 1 × 2 + 1
= 3
bin_to_dec("110")
= 3 × 2 + 0
= 6
bin_to_dec("1101")
= 6 × 2 + 1
= 13

(d) Hexadecimal numbers to decimal numbers

Non-Recursive

# Non-Recursive
HEX_DIGITS = "0123456789ABCDEF"
 
def hex_to_dec(number):
    answer = 0
 
    for i in number:
        value = HEX_DIGITS.index(i)
        answer = answer * 16 + value
 
    return answer

Recursive

# Recursive
HEX_DIGITS = "0123456789ABCDEF"
 
def hex_to_dec(number):
    # base case
    if len(number) == 1:
        return HEX_DIGITS.index(number)
 
    # recursive case
    return hex_to_dec(number[:-1]) * 16 + HEX_DIGITS.index(number[-1])

(e) Binary numbers to hexadecimal numbers

Use Mapping:

since there are only 16 variations

HEX_DIGITS = "0123456789ABCDEF"
 
BINARY_NIBBLES = [
    "0000", "0001", "0010", "0011",
    "0100", "0101", "0110", "0111",
    "1000", "1001", "1010", "1011",
    "1100", "1101", "1110", "1111"
]
 

Non-Recursive

# Non-Recursive
 
# Mapping
HEX_DIGITS = "0123456789ABCDEF"
 
BINARY_NIBBLES = [
    "0000", "0001", "0010", "0011",
    "0100", "0101", "0110", "0111",
    "1000", "1001", "1010", "1011",
    "1100", "1101", "1110", "1111"
]
 
# Code
def bin_to_hex(number):
    # Step 1: add leading zeroes
    while len(number) % 4 != 0:
        number = "0" + number
 
    answer = ""
 
    # Step 2: process four digits at a time
    for i in range(0, len(number), 4):
        nibble = number[i:i + 4]
 
        # Step 3: convert the nibble into a value 
        # (value is a digit 0-15)
        value = BINARY_NIBBLES.index(nibble)
 
        # Step 4: convert the value into a hexadecimal digit
        hex_digit = HEX_DIGITS[value]
 
        # Step 5: add the digit to the answer
        answer = answer + hex_digit
 
    return answer
 
Trace "101101"

使用 bin_to_hex("101101") 作为 test case。

Step 1 — Add leading zeroes

"101101" 的 length 是 6,不是 4 的倍数,所以 while loop 在左边加入 leading zeroes:

101101    length 6
0101101   length 7
00101101  length 8

现在 8 % 4 == 0,所以 while loop 停止,number 是 "00101101"。

Step 2 — Initialise the accumulator

answer = ""

answer 是 accumulator,用来逐个储存转换完成的 hexadecimal digits。

Step 3 — Process one nibble per iteration

for i in range(0, len(number), 4):

此时相当于 range(0, 8, 4),所以 i 依次是 0 和 4。number[i:i + 4] 从 index i 开始读取四个 digits;ending index 不包括在 slice 内。

iterationinumber[i:i + 4]valuehex_digitanswer
10"0010"2"2""2"
24"1101"13"D""2D"

First iteration:

number = 0 0 1 0 1 1 0 1
index    0 1 2 3 4 5 6 7
         └─────┘
          "0010"
 
BINARY_NIBBLES.index("0010") = 2
HEX_DIGITS[2] = "2"
answer = "" + "2" = "2"

Second iteration:

BINARY_NIBBLES.index("1101") = 13
HEX_DIGITS[13] = "D"
answer = "2" + "D" = "2D"

Final result

101101₂ → 0010 1101₂ → 2D₁₆

因此 return answer 返回 "2D"。

Recursive

# Recursive
HEX_DIGITS = "0123456789ABCDEF"
 
BINARY_NIBBLES = [
    "0000", "0001", "0010", "0011",
    "0100", "0101", "0110", "0111",
    "1000", "1001", "1010", "1011",
    "1100", "1101", "1110", "1111"
]
 
def bin_to_hex_recursive(number):
    # base case
    if len(number) <= 4:
        nibble = number.zfill(4) # zfill(4) add leading 0
        value = BINARY_NIBBLES.index(nibble)
        return HEX_DIGITS[value]
    
    # recursive case
    previous_digits = number[:-4]
    last_nibble = number[-4:]
    
	value = BINARY_NIBBLES.index(last_nibble)
    hex_digit = HEX_DIGITS[value] 
    
    return bin_to_hex_recursive(previous_digits) + hex_digit

(f) Hexadecimal numbers to binary numbers

Non-Recursive

# Non-Recursive
 
# Mapping
HEX_DIGITS = "0123456789ABCDEF"
 
BINARY_NIBBLES = [
    "0000", "0001", "0010", "0011",
    "0100", "0101", "0110", "0111",
    "1000", "1001", "1010", "1011",
    "1100", "1101", "1110", "1111"
]
 
answer = "" # DONT FORGET THIS
 
def hex_to_bin(number):
	for i in number:
		value = HEX_DIGITS.index(i)
		answer = answer + BINARY_NIBBLES[value]
	return answer

Recursive

# Recursive
HEX_DIGITS = "0123456789ABCDEF"
 
BINARY_NIBBLES = [
    "0000", "0001", "0010", "0011",
    "0100", "0101", "0110", "0111",
    "1000", "1001", "1010", "1011",
    "1100", "1101", "1110", "1111"
]
 
def hex_to_bin(number):
	# base case
	if len(number) == 1:
		value = HEX_DIGITS.index(number)
		return BINARY_NIBBLES[value]
 
	# recursive case
    previous_digits = number[:-1] # how it reduces to the base case by kicking out the last digit
    last_digit = number[-1]
    value = HEX_DIGITS.index(last_digit)
 
    return hex_to_bin(previous_digits) + BINARY_NIBBLES[value]
		
 
Trace hex_to_bin("2AF")
Call 1
hex_to_bin("2AF")

检查 base case:

len("2AF") == 1  # False

执行 recursive case:

previous_digits = "2A"
last_digit = "F"
value = 15

因此:

hex_to_bin("2AF")
= hex_to_bin("2A") + "1111"

但 hex_to_bin("2A") 还没有答案,所以这个 function call 会在 call stack 中等待。

Call 2
hex_to_bin("2A")

检查:

len("2A") == 1  # False

执行:

previous_digits = "2"
last_digit = "A"
value = 10

因此:

hex_to_bin("2A")
= hex_to_bin("2") + "1010"

它也要等待 hex_to_bin("2") 的答案。

Call 3:到达 base case
hex_to_bin("2")

检查:

len("2") == 1  # True

所以:

value = HEX_DIGITS.index("2")  # 2
return BINARY_NIBBLES[2]       # "0010"

现在不再产生新的 recursive call,开始返回答案。

从 call stack 返回

hex_to_bin("2") 返回:

"0010"

所以等待中的 hex_to_bin("2A") 可以完成:

hex_to_bin("2A")
= hex_to_bin("2") + "1010"
= "0010" + "1010"
= "00101010"

然后最外层也可以完成:

hex_to_bin("2AF")
= hex_to_bin("2A") + "1111"
= "00101010" + "1111"
= "001010101111"

最终答案:

hex_to_bin("2AF")  # "001010101111"
整个过程的简写
hex_to_bin("2AF")
= hex_to_bin("2A") + "1111"
= hex_to_bin("2") + "1010" + "1111"
= "0010" + "1010" + "1111"
= "001010101111"

Revision checklist

  • 02.1 Explain bit, byte, nibble, base and positional value.
  • 02.2 Convert positive integers between denary, binary and hexadecimal in all six directions, showing working.
  • 02.3 Group binary digits into nibbles and preserve required leading zeroes.
  • 02.4 State suitable computing applications and advantages of hexadecimal.
  • 02.5 Write iterative base-conversion code without relying on a conversion shortcut when an algorithm is requested.
  • 02.6 Trace or write a recursive conversion and distinguish a number from a string representation.
  • 02.7 Explain the need for character encoding and compare standard ASCII with Unicode.
  • 02.8 Use ord() and chr() correctly in a supplied character-processing algorithm.
  • 02.9 Test conversion code using zero if allowed, powers of a base, single digits and multi-digit values.

Visual revision mindmap

Chapter 02 revision mindmap

Open this mindmap and its text version · All 21 mindmaps

Your mindmap framework

Centre: Data representation and character encoding. Build the six branches below. For each subbranch, add a short definition, a labelled sample and one exam trap from memory; then check the chapter.

flowchart LR
    C["02 • Revision map"]
    C --> B0["Bits and place value"]
    C --> B1["Conversion routes"]
    C --> B2["Conversion algorithms"]
    C --> B3["Computing applications"]
    C --> B4["Characters and encoding"]
    C --> B5["Visual checks and mistakes"]
  • Bits and place value

    • Bit, nibble, byte; 1 nibble = 4 bits.
    • Bases 2, 10 and 16; digits allowed in each.
    • Digit contribution = digit × base to position.
    • Unsigned n-bit range: 0 through 2ⁿ−1.
  • Conversion routes

    • Binary/hex to denary: sum weighted digits.
    • Denary to binary/hex: repeated division and reverse remainders.
    • Binary ↔ hex: one four-bit group per hex digit.
    • Fixed width: preserve leading zeroes and label the base.
  • Conversion algorithms

    • Input contract: integer quantity or digit string.
    • Iterative state: quotient, remainder, output.
    • Recursive state: smaller quotient and returned text.
    • Hex-string mapping: digit → nibble → concatenation.
  • Computing applications

    • Decimal: human-facing quantities.
    • Binary: digital data and instructions.
    • Hex: compact addresses and RGB notation.
    • Same value, different representation; storage versus display.
  • Characters and encoding

    • ASCII: 7-bit codes, 128 characters.
    • Unicode: code points across scripts and symbols.
    • UTF-8: variable-length byte encoding.
    • ord and chr: code point ↔ Python character.
  • Visual checks and mistakes

    • Draw place-value columns for one conversion.
    • Draw 0A → 0000 1010 to preserve width.
    • Test zero, a base power, leading zeroes, several digits.
    • Avoid: code point = encoded bytes; Unicode always 16 bits.

Close the notes and test the map: explain one branch aloud, sketch its sample, then answer a linked practice question. Mark any missing link to revisit.

Source trail

9569 §§3.1–3.2; school Data Representation PDF pp.1–6; Python Ch.2 character types.

HCI 2023 Q4; 2024 Q6; 2025 Q2(e)(ii), Q3(b).

Source guide records provenance and original-paper locations.