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
import math
print(math.pi)
print(math.e)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.
# 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))Area: 153.94 Circumference: 43.98
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
import math
print(math.sqrt(25))
print(math.sqrt(2))
print(math.sqrt(0))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)).
import math
print(math.sqrt(-9))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()
import math
print(math.pow(2, 10))
print(2 ** 10)
print(math.pow(9, 0.5))1024.0 1024 3.0
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.import math
print(math.fabs(-7))
print(math.fabs(7))
print(abs(-7))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
import math
print(math.ceil(4.1))
print(math.ceil(4.9))
print(math.floor(4.1))
print(math.floor(4.9))5 5 4 4
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.
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.A carton holds 6 items. How many cartons are needed for 47 items?
# 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))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:
import math
print(math.sin(90))0.8939966636005579
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:
# 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))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:
import math
print(math.sin(math.radians(30)))
print(math.cos(math.radians(60)))
print(math.tan(math.radians(45)))0.49999999999999994 0.5000000000000001 0.9999999999999999
==: math.sin(math.radians(30)) == 0.5 is False.6Three programs worth having
Find the hypotenuse of a right triangle with sides 3 and 4.
import math
a = 3
b = 4
print('Hypotenuse:', math.sqrt(math.pow(a, 2) + math.pow(b, 2)))Hypotenuse: 5.0
Find the area of a triangle from its three sides, using Heron's formula.
# 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)))Area: 6.0
Solve a quadratic equation, when it has real roots.
# 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)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.
7Recap
| Name | Example | Gives | Type |
|---|---|---|---|
math.pi | math.pi | 3.141592653589793 | float |
math.e | math.e | 2.718281828459045 | float |
sqrt() | math.sqrt(25) | 5.0 | float |
pow() | math.pow(2, 10) | 1024.0 | float |
fabs() | math.fabs(-7) | 7.0 | float |
ceil() | math.ceil(4.1) | 5 | int |
floor() | math.floor(4.9) | 4 | int |
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 |
- 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 notint(). - 2
Read a number and print its square root, refusing negatives politely.
Hint ·
if n < 0:before themath.sqrt(), so the domain error never happens. - 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
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, andround(..., 4)on the way out. - 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.
Why does math.sin(90) not print 1.0?
47 items, 6 to a carton. Which line gives the number of cartons?
What does math.floor(-4.1) give?