The math module provides access to the mathematical functions defined by the C standard. It operates on floats (for complex numbers, use cmath), and includes constants like pi and e, plus functions for rounding, powers, logarithms, trigonometry, and combinatorics.
Python’s built-in calculator
What you will learn: the math constants, square roots and powers, and the rounding functions floor, ceil, and trunc.
How to read this tab: Note that math.pow always returns a float while ** keeps integer types — prefer ** for integer powers.
The math module is Python's calculator — square roots, powers, logarithms, trigonometry, and constants like pi. It works on regular numbers (floats) and is part of the standard library, so there is nothing to install.
Prefer ** over math.pow for integer powers. 2 ** 10 gives int 1024 and is faster; math.pow(2, 10) gives float 1024.0. Use math.pow only when you specifically want a float.
Constants and basic functions
import math
# Constants
print(math.pi) # 3.141592653589793
print(math.e) # 2.718281828459045
print(math.inf) # infinity
print(math.nan) # not-a-number
# Square root and powers
print(math.sqrt(144)) # 12.0
print(math.pow(2, 10)) # 1024.0 (always returns a float)
print(2 ** 10) # 1024 (** keeps int type — often better)
# Absolute value and sign
print(math.fabs(-5)) # 5.0 (always float; abs(-5) gives int 5)
print(math.copysign(3, -1)) # -3.0 (magnitude of 3, sign of -1)math.pow(2, 10) always returns a float (1024.0). The ** operator preserves int types (2 ** 10 gives int 1024). For integer powers, prefer **; it is faster and keeps the type. Use math.pow only when you specifically want a float result.
floor/ceil/trunc differ on negatives. floor(-4.1) is -5 (toward negative infinity); trunc(-4.1) is -4 (toward zero); ceil(-4.7) is -4. Know which direction you need. Also note built-in round() uses banker’s rounding: round(2.5) is 2, not 3.
Rounding and powers
import math
# floor rounds DOWN, ceil rounds UP — both return int
print(math.floor(4.7)) # 4
print(math.ceil(4.1)) # 5
print(math.floor(-4.1)) # -5 (down means toward negative infinity)
print(math.ceil(-4.7)) # -4
# trunc chops toward zero (drops the fractional part)
print(math.trunc(4.9)) # 4
print(math.trunc(-4.9)) # -4 (toward zero, not down)
# The built-in round() uses banker's rounding (round half to even)
print(round(2.5)) # 2 (not 3!) — rounds to even
print(round(3.5)) # 4 — also rounds to even
print(round(3.14159, 2)) # 3.14 — round to 2 decimal places
# Factorials and combinatorics
print(math.factorial(5)) # 120
print(math.comb(10, 3)) # 120 — combinations "10 choose 3"
print(math.perm(10, 3)) # 720 — permutations
print(math.gcd(48, 36)) # 12 — greatest common divisor
print(math.lcm(4, 6)) # 12 — least common multiple (3.9+)✅ Beginner tab complete
- I can use math.pi, math.e, math.sqrt
- I know math.pow returns a float but ** keeps int type
- I know floor rounds down, ceil rounds up, trunc chops toward zero
- I can use factorial, comb, perm, gcd, lcm
Trigonometry, logs, and float safety
What you will learn: trig functions (in radians!), logarithms, and the float-safety tools isclose, fsum, and isqrt.
How to read this tab: The radians-not-degrees rule causes endless bugs — always convert with math.radians() first.
Trig functions use radians, not degrees. math.sin(90) is the sine of 90 radians, not 90 degrees. Convert first: math.sin(math.radians(90)). This is one of the most common math bugs.
Trigonometry and logarithms
Trig functions work in radians, not degrees — a frequent source of bugs. Use math.radians() and math.degrees() to convert.
import math
# Trig functions take RADIANS, not degrees
print(math.sin(math.pi / 2)) # 1.0 (sin of 90 degrees)
print(math.cos(0)) # 1.0
# Convert between degrees and radians
print(math.radians(180)) # 3.14159... (180 degrees in radians)
print(math.degrees(math.pi)) # 180.0
# To take sin of 30 DEGREES, convert first:
print(math.sin(math.radians(30))) # 0.5
# Logarithms
print(math.log(math.e)) # 1.0 (natural log, base e)
print(math.log(8, 2)) # 3.0 (log base 2)
print(math.log10(1000)) # 3.0 (base 10)
print(math.log2(1024)) # 10.0 (base 2, most accurate)
# Exponential
print(math.exp(1)) # 2.718... (e^1)
# Hypotenuse — distance from origin (handles 2D and N-D)
print(math.hypot(3, 4)) # 5.0
print(math.dist((0, 0), (3, 4))) # 5.0 (distance between points, 3.8+)Never compare floats with ==. 0.1 + 0.2 == 0.3 is False due to binary representation. Use math.isclose(a, b). For accurate summation use math.fsum; for exact integer square roots use math.isqrt.
Float safety functions
Floating-point arithmetic is imprecise. The math module provides tools to compare and check floats safely.
import math
# The classic float problem
print(0.1 + 0.2) # 0.30000000000000004
print(0.1 + 0.2 == 0.3) # False!
# isclose — the RIGHT way to compare floats
print(math.isclose(0.1 + 0.2, 0.3)) # True
# Check for special values
print(math.isnan(float("nan"))) # True
print(math.isinf(float("inf"))) # True
print(math.isfinite(42.0)) # True
# fsum — accurate summation of floats (avoids accumulated error)
values = [0.1] * 10
print(sum(values)) # 0.9999999999999999
print(math.fsum(values)) # 1.0 — accurate
# isqrt — integer square root, exact, no float error (3.8+)
print(math.isqrt(99)) # 9 (floor of the exact square root)math.sin(90) does NOT give the sine of 90 degrees — it gives the sine of 90 radians. Convert first: math.sin(math.radians(90)).math.isclose(a, b). Floating-point arithmetic accumulates tiny errors, so 0.1 + 0.2 == 0.3 is False. isclose checks "close enough" within a tolerance.✅ Intermediate tab complete
- I know trig functions take radians, and convert with math.radians()
- I can use log, log2, log10
- I never compare floats with == — I use math.isclose
- I can use fsum for accurate summation and isqrt for exact integer roots
IEEE 754 and choosing a numeric type
What you will learn: why 0.1 cannot be stored exactly, isclose tolerances, and when to reach for decimal or fractions instead of float.
How to read this tab: Read when float precision matters — money needs Decimal, exact ratios need Fraction.
IEEE 754, the float spec, and when to use decimal
Python floats are IEEE 754 double-precision (64-bit) binary floating-point numbers. Because they store values in binary, decimal fractions like 0.1 cannot be represented exactly — 0.1 is actually stored as the nearest representable binary value, which is slightly more than 0.1. This is not a Python flaw; it is inherent to binary floating-point and behaves identically in C, Java, and JavaScript.
import math
from decimal import Decimal
from fractions import Fraction
# See the true stored value of 0.1
print(f"{0.1:.20f}") # 0.10000000000000000555
# math.isclose with explicit tolerances
print(math.isclose(1000.0, 1000.1, rel_tol=1e-3)) # True (0.1% rel)
print(math.isclose(0.0, 1e-10, abs_tol=1e-9)) # True (abs for near-zero)
# rel_tol is relative (good for large numbers);
# abs_tol is absolute (needed when comparing against zero)
# For EXACT decimal arithmetic (money!), use Decimal
print(Decimal("0.1") + Decimal("0.2")) # Decimal('0.3') — exact
print(Decimal("0.1") + Decimal("0.2") == Decimal("0.3")) # True
# For EXACT rational arithmetic, use Fraction
print(Fraction(1, 3) + Fraction(1, 6)) # Fraction(1, 2) — exact
# math.frexp / math.ldexp — decompose a float into mantissa & exponent
m, e = math.frexp(8.0)
print(m, e) # 0.5 4 -> 0.5 * 2**4 == 8.0
print(math.ldexp(m, e)) # 8.0 — reconstruct
# math.nextafter (3.9+) — the next representable float
print(math.nextafter(1.0, 2.0)) # 1.0000000000000002 (smallest step up)The practical rule for choosing a numeric type: use float (and math) for scientific and engineering work where tiny relative errors are acceptable; use decimal.Decimal for money and any domain requiring exact decimal representation; use fractions.Fraction for exact rational arithmetic. The math.isclose function with appropriately chosen rel_tol and abs_tol is the correct tool for comparing floats — relative tolerance for general magnitudes, absolute tolerance when one value may be zero.
✅ Expert tab complete
- I know Python floats are IEEE 754 double-precision binary
- I know why 0.1 is not stored exactly
- I can choose rel_tol vs abs_tol in math.isclose
- I use Decimal for money and Fraction for exact ratios