🐍 Python Course · Stage 3 · Lesson 44 of 89 · math — Math Functions
Course Overview →

🔵 Read Beginner and Intermediate tabs. The Expert tab has the internals — worth reading once you finish Stage 3.

← argparse — CLI Arguments random — Random Numbers →
thecodex.expert · The Codex Family of Knowledge
Python Standard Library

math — Mathematical Functions

The math module provides the standard mathematical functions and constants — trigonometry, logarithms, factorials, and reliable tools for floating-point work.

import math docs.python.org Last verified:
Canonical Definition

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.

🟩 Beginner

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.

⏱ 25 min📄 2 sections🔶 Prerequisite: Modules & Packages
The idea

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

Pythonbasics.py
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 vs the ** operator

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

Pythonrounding.py
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

Continue to random →

🔵 Intermediate

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.

⏱ 25 min📄 2 sections🔶 Prerequisite: Beginner tab

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.

Pythontrig_logs.py
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.

Pythonfloat_safety.py
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)
Commonly confused
Trig functions use radians. 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)).
Never compare floats with ==. Use 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

Continue to random →

🔴 Expert

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.

⏱ 20 min📄 1 section🔶 Prerequisite: After Stage 3

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.

Pythonieee754.py
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

Continue to random →

Sources

1
Python Standard Library — math. docs.python.org/3/library/math.html.
2
Python Tutorial — Floating-Point Arithmetic: Issues and Limitations. docs.python.org/3/tutorial/floatingpoint.html.
3
Python Standard Library — decimal (exact decimal arithmetic). docs.python.org/3/library/decimal.html.
4
Python Standard Library — math.isclose. docs.python.org/3/library/math.html#math.isclose.
Source confidence: High Last verified: Primary source: docs.python.org math