Number sets, bases and units
Natural, integer, rational, irrational, real and ordinal numbers; any number base; bits, bytes, kilo and kibi.
Do this lesson in the simulatorAt GCSE you converted between binary, denary and hexadecimal and met the units from bits to petabytes. At A level the same ideas come with exact vocabulary: which set a number belongs to, what a base really is, and the difference between a kilobyte and a kibibyte. Examiners mark these words precisely, so this lesson pins them down.
Number sets
Mathematicians group numbers into sets, each written with a letter:
| Set | Symbol | What it holds | Examples |
|---|---|---|---|
| Natural numbers | ℕ | the whole numbers from 0 upwards | 0, 1, 2, 51 |
| Integers | ℤ | whole numbers, positive, negative or zero | -128, 0, 127 |
| Rational numbers | ℚ | any number that can be written as a fraction p/q of two integers, q not 0 | 3/4, -0.5, 0.1, 7 |
| Irrational numbers | real numbers that cannot be written as a fraction | √2, π | |
| Real numbers | ℝ | every rational and irrational number: every point on the number line | 0.51, -2.5, π |
Each set sits inside the next: every natural number is an integer, every integer is rational (7 is 7/1), and every rational number is real. Irrational numbers are the reals that are not rational. The AQA specification defines ℕ as starting at 0, so learn it that way.
An irrational number's decimal expansion never ends and never repeats. A computer has a fixed number of bits, so it can only ever store an approximation of √2 or π. Lesson A7.4 is about the errors that causes.
Ordinal numbers describe position in an order: first, second, third. When the robot visits waypoints, the waypoint's ordinal tells you which one in the sequence it is, not how many there are. A list index is an ordinal idea: route[0] is the first item.
Counting and measurement
The two sets you use most in programs are chosen by what you are doing:
- Counting uses natural numbers. The robot has bumped 3 times, there are 64 cells in its ToF grid. You cannot bump 2.7 times.
- Measurement uses real numbers. The wall is 51.3 cm away, the battery is at 3.82 V. Between any two measurements there is always another possible one.
That is why a counter is an int in Python and a sensor reading is a float. Choosing the wrong one is a design error: a distance stored as an integer silently throws away everything after the point.
Number bases
A base is how many different digits a place can hold, and each place is worth base times the place to its right. In base 10 the places are 1, 10, 100; in base 2 they are 1, 2, 4, 8; in base 16 they are 1, 16, 256.
| Base | Name | Digits | 173 written in it |
|---|---|---|---|
| 2 | binary | 0 1 | 10101101 |
| 8 | octal | 0 to 7 | 255 |
| 10 | denary (decimal) | 0 to 9 | 173 |
| 16 | hexadecimal | 0 to 9, A to F | AD |
When bases are mixed, write the base as a subscript: 10101101₂ = 173₁₀ = AD₁₆.
To convert from any base, multiply each digit by its place value and add. To convert to any base, divide by the base repeatedly: each remainder is a digit, starting with the rightmost.
173 to hexadecimal: 173 ÷ 16 = 10 remainder 13 (D), then 10 ÷ 16 = 0 remainder 10 (A). Reading the remainders from last to first gives AD.
# place values: any base back to denary
DIGITS = "0123456789ABCDEF"
def from_base(text, base):
value = 0
for ch in text:
value = value * base + DIGITS.index(ch) # shift one place left, add the new digit
return value
print(from_base("10101101", 2), from_base("255", 8), from_base("AD", 16))
print(int("AD", 16)) # Python's built-in does the same job
Hexadecimal is used as a shorthand for binary because 16 is 2⁴: each hex digit stands for exactly four bits. Colour codes, memory addresses and the bytes in a radio packet are all easier to read and harder to mistype in hex. The computer still stores binary; hex is for people.
Bits, bytes and units
A bit is one binary digit. A byte is a group of 8 bits. With n bits you can make 2ⁿ different patterns, so 8 bits give 256 and 16 bits give 65,536. Turned the other way: to give each of 64 ToF cells its own number you need 6 bits, since 2⁶ = 64.
Storage sizes use two families of prefixes, and they are not the same:
| Decimal prefix | Value | Binary prefix | Value |
|---|---|---|---|
| kilo (kB) | 10³ = 1,000 | kibi (KiB) | 2¹⁰ = 1,024 |
| mega (MB) | 10⁶ | mebi (MiB) | 2²⁰ |
| giga (GB) | 10⁹ | gibi (GiB) | 2³⁰ |
| tera (TB) | 10¹² | tebi (TiB) | 2⁴⁰ |
The binary prefixes exist because memory comes in powers of two and people used "kilobyte" loosely for both values. The difference grows with size: 1 GiB is about 7% more than 1 GB, and 1 TiB about 10% more than 1 TB. That is why a "1 TB" drive shows as about 931 GiB.
frame_bits = 320 * 240 * 24 # one camera frame at 24 bits per pixel
frame_bytes = frame_bits // 8
print(frame_bytes, "bytes")
print(frame_bytes / 1000, "kB", frame_bytes / 1024, "KiB")
print(10 ** 12 / 2 ** 30, "GiB in a terabyte")
Task: any base
Write to_base(n, base). The parameter n is a whole number, 0 or more; base is a whole number from 2 to 16. It returns the digits of n in that base as a string, using 0 to 9 then A to F, and returns "0" when n is 0. Use repeated division with // and %: you may not use bin, hex, oct or format. The loop at the bottom prints lines such as 173 in base 16 = AD.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
DIGITS = "0123456789ABCDEF"
def to_base(n, base):
# repeated division goes here
return ""
for n, base in [(173, 2), (173, 8), (173, 16), (0, 2), (2024, 16)]:
print(n, "in base", base, "=", to_base(n, base))
Task: kilo or kibi
The robot's camera sees 320 by 240 pixels at 24 bits per pixel. Using the four constants, calculate and print exactly these five lines (the numbers must be calculated, not typed):
frame: <bytes> bytes, the size of one frame in bytes, as a whole numberframe: <n> kB, bytes divided by 1,000, thenround(value, 2)frame: <n> KiB, bytes divided by 1,024, thenround(value, 2)minute at 30 fps: <n> MB, the bytes in 60 seconds of frames divided by 1,000², thenround(value, 2)minute at 30 fps: <n> MiB, the same bytes divided by 1,024², thenround(value, 2)
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
WIDTH = 320
HEIGHT = 240
BITS_PER_PIXEL = 24
FPS = 30
frame_bytes = 0
print("frame:", frame_bytes, "bytes")
Challenges
- Extend
to_baseto refuse a base outside 2 to 16 by raising aValueError. - Write
from_basewithoutDIGITS.index, using character codes instead. - Which set does each belong to: the number of robots on the mat, the robot's heading in degrees, 22/7, the square root of 16?