LambdaLabTM
Computer Science · Class 11 · Python Modules
Modulesmath⏱️ 16 min read

The math Module

Everything you would otherwise reach for a calculator to do. Nine names are on your syllabus, and they divide neatly into three groups: two constants, four that do arithmetic, and three angles. The only one that really catches people out is the last group, and it catches everybody once.

1The two constants — pi and e

constants.py
import math

print(math.pi)
print(math.e)
Output
3.141592653589793
2.718281828459045

They are values, not functions — no brackets after them. math.pi() would raise TypeError: 'float' object is not callable, which is the error to expect if you type it out of habit.

circle.py
# area and circumference of a circle

import math

r = 7

print('Area:         ', round(math.pi * r * r, 2))
print('Circumference:', round(2 * math.pi * r, 2))
Output
Area:          153.94
Circumference: 43.98
Tip
math.pi beats writing 3.14. It carries fifteen decimal places rather than two, and — more usefully — math.pi in a formula says pi to whoever reads it, where 3.14 is just a number someone has to recognise.

2sqrt() — the square root

sqrt.py
import math

print(math.sqrt(25))
print(math.sqrt(2))
print(math.sqrt(0))
Output
5.0
1.4142135623730951
0.0

Note 5.0, not 5. math.sqrt() always answers with a float, even when the answer is a whole number. If a whole number is what you want to print, wrap it: int(math.sqrt(25)).

sqrt_negative.py
import math

print(math.sqrt(-9))
Output
Traceback (most recent call last):
  File "sqrt_negative.py", line 3, in <module>
    print(math.sqrt(-9))
          ^^^^^^^^^^^^^
ValueError: math domain error

“Domain error” means that input is outside what this function accepts. A negative number has no real square root, so any program that takes a number from a user and roots it needs a check first.

3pow() and fabs()

pow.py
import math

print(math.pow(2, 10))
print(2 ** 10)
print(math.pow(9, 0.5))
Output
1024.0
1024
3.0
Key Takeaway
math.pow() always gives a float; ** keeps whole numbers whole. Both raise 2 to the 10th and they print differently — 1024.0 against 1024. ** is the operator you already know and the one to prefer; math.pow() is on the syllabus, so recognise it.
fabs.py
import math

print(math.fabs(-7))
print(math.fabs(7))
print(abs(-7))
Output
7.0
7.0
7

fabs() is “float absolute” — the size of a number with the sign thrown away, always as a float. Python's built-in abs() does the same job and keeps the type it was given, which is why abs(-7) prints 7 and math.fabs(-7) prints 7.0.

4ceil() and floor() — rounding with a direction

ceil_floor.py
import math

print(math.ceil(4.1))
print(math.ceil(4.9))
print(math.floor(4.1))
print(math.floor(4.9))
Output
5
5
4
4
Ceiling and floor, literally
ceil↑ always up4.1 → 5 · 4.9 → 5 · −4.1 → −4
floor↓ always down4.1 → 4 · 4.9 → 4 · −4.1 → −5

Watch the negatives. “Up” means towards positive, so ceil(-4.1) is -4 — a bigger number. And “down” takes floor(-4.1) to -5.

Both hand back an int, not a float — which is the opposite of most of this module, and convenient, because the answers are usually counts of something.

Watch Out
ceil() is not round(). round(4.4) is 4 and math.ceil(4.4) is 5 — rounding picks the nearer, ceiling always goes up. round() has a surprise of its own too: round(4.5) is 4, not 5, because Python breaks an exact half towards the even number. When you mean “always upwards”, say ceil and the question does not arise.
📋 The problem

A carton holds 6 items. How many cartons are needed for 47 items?

boxes.py
# 47 / 6 is 7.83 — and you cannot ship 0.83 of a carton

import math

items = 47
per_box = 6

print('Boxes needed:', math.ceil(items / per_box))
Output
Boxes needed: 8

This is what ceil() is for. Anything counted in whole containers — cartons, buses, pages, rounds of a tournament — rounds up, because a part-full one still costs a whole one.

5sin(), cos(), tan() — and radians

You know that sin 90° is 1. Python does not agree:

trig_trap.py
import math

print(math.sin(90))
Output
0.8939966636005579
Watch Out
These functions measure angles in radians, not degrees. math.sin(90) is the sine of 90 radians, which is a perfectly good number and not the one you wanted. Nothing warns you, because nothing is wrong — you asked a different question.

math.radians() converts, and it goes inside the call:

trig.py
# degrees in, radians out, then the ratio

import math

print('sin 30 =', round(math.sin(math.radians(30)), 4))
print('cos 60 =', round(math.cos(math.radians(60)), 4))
print('tan 45 =', round(math.tan(math.radians(45)), 4))
Output
sin 30 = 0.5
cos 60 = 0.5
tan 45 = 1.0

The round() is not decoration either. Without it, the same three lines print this:

trig_raw.py
import math

print(math.sin(math.radians(30)))
print(math.cos(math.radians(60)))
print(math.tan(math.radians(45)))
Output
0.49999999999999994
0.5000000000000001
0.9999999999999999
Key Takeaway
Those are not mistakes — they are floats. A computer stores decimals in binary, and a value like 0.5 arrived at through a conversion and a trigonometric series lands a hair either side of it. Every language does this. The fix is to round when you print, and never to test a float with ==: math.sin(math.radians(30)) == 0.5 is False.

6Three programs worth having

📋 The problem

Find the hypotenuse of a right triangle with sides 3 and 4.

hypotenuse.py
import math

a = 3
b = 4

print('Hypotenuse:', math.sqrt(math.pow(a, 2) + math.pow(b, 2)))
Output
Hypotenuse: 5.0
📋 The problem

Find the area of a triangle from its three sides, using Heron's formula.

triangle_area.py
# s is the semi-perimeter; the area is the root of s(s-a)(s-b)(s-c)

import math

a, b, c = 3, 4, 5
s = (a + b + c) / 2

print('Area:', math.sqrt(s * (s - a) * (s - b) * (s - c)))
Output
Area: 6.0
📋 The problem

Solve a quadratic equation, when it has real roots.

quadratic.py
# x^2 - 5x + 6 = 0

import math

a, b, c = 1, -5, 6
d = b * b - 4 * a * c

if d < 0:
    print('No real roots')
else:
    r1 = (-b + math.sqrt(d)) / (2 * a)
    r2 = (-b - math.sqrt(d)) / (2 * a)
    print('Roots:', r1, 'and', r2)
Output
Roots: 3.0 and 2.0

The if d < 0: is the guard that stops math.sqrt() raising its domain error. Checking before calling is the whole technique, and it is the same shape as checking a key is in a dictionary before reaching for it.

quadratic.py

7Recap

NameExampleGivesType
math.pimath.pi3.141592653589793float
math.emath.e2.718281828459045float
sqrt()math.sqrt(25)5.0float
pow()math.pow(2, 10)1024.0float
fabs()math.fabs(-7)7.0float
ceil()math.ceil(4.1)5int
floor()math.floor(4.9)4int
sin()math.sin(math.radians(30))0.4999999…float
cos()math.cos(math.radians(60))0.5000000…float
tan()math.tan(math.radians(45))0.9999999…float
✍️ Now write these yourself
  1. 1

    Read a radius with input() and print the area and circumference, to two decimals.

    Hint · float(input(...)) — a radius can be 2.5, so not int().

  2. 2

    Read a number and print its square root, refusing negatives politely.

    Hint · if n < 0: before the math.sqrt(), so the domain error never happens.

  3. 3

    A bus holds 40 students. Print how many buses a trip of 250 students needs.

    Hint · math.ceil(250 / 40). The last bus is not full and still has to be hired.

  4. 4

    Print a small table of sin, cos and tan for 0°, 30°, 45°, 60°.

    Hint · A tuple of angles, one loop, math.radians() inside each call, and round(..., 4) on the way out.

  5. 5

    Print the distance between two points, given their four coordinates.

    Hint · math.sqrt(math.pow(x2 - x1, 2) + math.pow(y2 - y1, 2)). Try (2, 3) and (7, 15) — the answer is a whole number.

Quick Check

Why does math.sin(90) not print 1.0?

Quick Check

47 items, 6 to a carton. Which line gives the number of cartons?

Quick Check

What does math.floor(-4.1) give?