Math #
Python provides several modules for numerical computing, each with a different purpose. The math module handles common float-based mathematical operations, decimal for calculations requiring high precision like finance, fractions for exact rational number arithmetic, and statistics for basic data analysis. Understanding the differences between these four modules is important so you don’t get stuck using the wrong tool for different problems.
Why Floats Can Be Problematic #
Before diving into the modules, it’s important to understand one Python behavior that often surprises beginners: float arithmetic isn’t always exact.
# Surprising float behavior
print(0.1 + 0.2) # 0.30000000000000004 -- not 0.3!
print(0.1 + 0.2 == 0.3) # False
# This isn't a Python bug -- it's the nature of IEEE 754 floats in every language
# ANTI-PATTERN: comparing floats directly
if total_harga == 150000.0: # can be wrong due to rounding errors
pass
# CORRECT: use a tolerance or the math.isclose() module
import math
if math.isclose(total_harga, 150000.0, rel_tol=1e-9):
pass
# Or use the decimal module for financial calculations (covered below)
Don’t usefloatfor financial calculations or anything requiring exact decimal precision. Use thedecimalmodule instead.
The math Module — General Mathematical Operations
#
The math module provides mathematical functions operating on float types. Suitable for scientific computation, geometry, and general technical calculations.
Rounding and Absolute Value #
import math
# Various ways to round
print(math.ceil(4.2)) # 5 -- round up
print(math.ceil(-4.2)) # -4 -- up toward zero
print(math.floor(4.8)) # 4 -- round down
print(math.floor(-4.8)) # -5 -- away from zero
print(math.trunc(4.8)) # 4 -- truncate toward zero
print(math.trunc(-4.8)) # -4 -- truncate toward zero
# The difference between trunc and floor for negative numbers:
# trunc(-4.8) = -4 (toward zero)
# floor(-4.8) = -5 (down, away from zero)
# Absolute value
print(math.fabs(-5.5)) # 5.5 -- always float
print(abs(-5.5)) # 5.5 -- Python built-in, can be int or float
Roots, Powers, and Logarithms #
import math
# Roots and powers
print(math.sqrt(16)) # 4.0 -- square root
print(math.isqrt(17)) # 4 -- integer square root (rounded down)
print(math.pow(2, 10)) # 1024.0 -- always float
print(2 ** 10) # 1024 -- built-in operator, can be int
# Logarithms
print(math.log(8, 2)) # 3.0 -- log base 2 of 8
print(math.log(math.e)) # 1.0 -- natural log (base e)
print(math.log10(1000)) # 3.0 -- log base 10
print(math.log2(1024)) # 10.0 -- log base 2 (more precise than log(x, 2))
# Exponentials
print(math.exp(1)) # 2.718... -- e^1
print(math.exp(2)) # 7.389... -- e^2
Trigonometric Functions #
All trigonometric functions in math work in radians, not degrees. Use math.radians() and math.degrees() for conversion.
import math
# Angle conversion
print(math.radians(180)) # 3.141592... -- degrees to radians
print(math.degrees(math.pi)) # 180.0 -- radians to degrees
# Basic trigonometric functions (input in radians)
print(math.sin(math.pi / 2)) # 1.0 -- sin 90°
print(math.cos(math.pi)) # -1.0 -- cos 180°
print(math.tan(math.pi / 4)) # 1.0 -- tan 45°
# Inverse trigonometry (output in radians)
print(math.asin(1)) # 1.5707... -- π/2, i.e., 90°
print(math.acos(-1)) # 3.1415... -- π, i.e., 180°
print(math.atan(1)) # 0.7853... -- π/4, i.e., 45°
# atan2 -- safer than atan for determining the quadrant
print(math.atan2(1, 1)) # 0.7853... -- angle from point (1,1) to the origin
print(math.atan2(-1, 1)) # -0.785... -- angle from point (1,-1) to the origin
Combinatorial Functions #
import math
# Factorial
print(math.factorial(5)) # 120 -- 5! = 5×4×3×2×1
print(math.factorial(0)) # 1 -- 0! = 1 by definition
# Combination C(n, k) -- choosing k items from n without order
print(math.comb(5, 2)) # 10 -- C(5,2)
print(math.comb(10, 3)) # 120
# Permutation P(n, k) -- choosing k items from n with order
print(math.perm(5, 2)) # 20 -- P(5,2)
print(math.perm(5)) # 120 -- P(5,5) = 5!
Utility Functions #
import math
# Euclidean distance / vector length
print(math.hypot(3, 4)) # 5.0 -- √(3²+4²)
print(math.hypot(1, 1, 1)) # √3 -- supports n dimensions (Python 3.8+)
# GCD and LCM
print(math.gcd(12, 8)) # 4 -- greatest common divisor
print(math.lcm(4, 6)) # 12 -- least common multiple (Python 3.9+)
print(math.gcd(12, 8, 6)) # 2 -- GCD of more than two numbers (Python 3.9+)
# Float comparison with tolerance
print(math.isclose(0.1 + 0.2, 0.3)) # True -- with the default relative tolerance
print(math.isclose(1000.0, 1001.0, rel_tol=0.01)) # True -- 1% tolerance
print(math.isclose(0.0, 1e-10, abs_tol=1e-9)) # True -- absolute tolerance
# Check special float values
print(math.isfinite(1.0)) # True
print(math.isfinite(math.inf)) # False
print(math.isinf(math.inf)) # True
print(math.isnan(math.nan)) # True
print(math.isnan(float("nan"))) # True
Mathematical Constants #
import math
print(math.pi) # 3.141592653589793 -- π
print(math.e) # 2.718281828459045 -- Euler's number
print(math.tau) # 6.283185307179586 -- τ = 2π
print(math.inf) # inf -- positive infinity
print(math.nan) # nan -- Not a Number
The decimal Module — High Precision
#
The decimal module implements decimal arithmetic with controllable precision. This is the right solution for financial calculations, accounting, or anything that must not have rounding errors.
from decimal import Decimal, getcontext
# Direct comparison
print(0.1 + 0.2) # 0.30000000000000004 -- regular float
print(Decimal("0.1") + Decimal("0.2")) # 0.3 -- exact precision
# ANTI-PATTERN: creating a Decimal from a float
print(Decimal(0.1)) # 0.1000000000000000055511151231257827021181583404541015625
# -- the float already lost precision before entering Decimal!
# CORRECT: create a Decimal from a string
print(Decimal("0.1")) # 0.1 -- precision preserved
Precision Control and Rounding #
from decimal import Decimal, getcontext, ROUND_HALF_UP, ROUND_DOWN
# Set the global precision (number of significant digits)
getcontext().prec = 10
hasil = Decimal("1") / Decimal("3")
print(hasil) # 0.3333333333 -- 10 significant digits
# Rounding with a specific method
harga = Decimal("1234.5678")
print(harga.quantize(Decimal("0.01"))) # 1234.57 -- ROUND_HALF_EVEN (default)
print(harga.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)) # 1234.57
print(harga.quantize(Decimal("0.01"), rounding=ROUND_DOWN)) # 1234.56
# Finance example: calculate the total purchase
def hitung_total(harga_satuan: str, jumlah: int, pajak_persen: str) -> Decimal:
harga = Decimal(harga_satuan)
pajak = Decimal(pajak_persen) / Decimal("100")
subtotal = harga * jumlah
total = subtotal * (1 + pajak)
return total.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
print(hitung_total("99.99", 3, "11")) # 332.97
Always create aDecimalfrom a string, not from a float.Decimal("0.1")produces exact0.1, whileDecimal(0.1)inherits the float’s imprecision.
The fractions Module — Rational Numbers
#
The fractions module represents numbers as exact fractions (numerator/denominator). Useful for mathematical calculations needing exact results without floating point errors.
from fractions import Fraction
# Create a Fraction from various sources
print(Fraction(1, 3)) # 1/3
print(Fraction("3/7")) # 3/7
print(Fraction("0.25")) # 1/4 -- convert from a decimal string
print(Fraction(0.25)) # 1/4 -- safe because 0.25 is exact in float
# Exact arithmetic
a = Fraction(1, 3)
b = Fraction(1, 6)
print(a + b) # 1/2 -- not 0.4999... or 0.5000001
print(a * b) # 1/18
print(a - b) # 1/6
print(a / b) # 2
# Access the numerator and denominator
f = Fraction(5, 6)
print(f.numerator) # 5
print(f.denominator) # 6
# Fractions are automatically simplified
print(Fraction(6, 4)) # 3/2 -- not 6/4
print(Fraction(10, 5)) # 2 -- not 10/5
The statistics Module — Basic Data Analysis
#
The statistics module provides ready-made descriptive statistics functions without needing extra libraries like NumPy.
import statistics
data = [4, 8, 6, 5, 3, 2, 8, 9, 2, 5]
# Measures of central tendency
print(statistics.mean(data)) # 5.2 -- average
print(statistics.median(data)) # 5.0 -- middle value (sorted first)
print(statistics.mode([1,1,2,3])) # 1 -- most frequent value
print(statistics.multimode([1,1,2,2,3])) # [1, 2] -- all modes (Python 3.8+)
# Measures of spread
print(statistics.stdev(data)) # 2.393... -- sample standard deviation
print(statistics.pstdev(data)) # 2.270... -- population standard deviation
print(statistics.variance(data)) # 5.733... -- sample variance
print(statistics.pvariance(data)) # 5.16 -- population variance
# Quantiles (Python 3.8+)
print(statistics.quantiles(data, n=4)) # quartiles Q1, Q2, Q3
When to Use statistics vs Other Libraries
#
Simple data analysis, no external dependencies?
✓ Use Python's built-in statistics module
Large datasets, performance matters, or need more functions?
✓ Use NumPy (pip install numpy)
Data analysis with DataFrames, groupby, or visualization?
✓ Use pandas (pip install pandas)
Machine learning or advanced statistics?
✓ Use scipy.stats (pip install scipy)
Choosing the Right Module #
General mathematical calculations (trigonometry, logarithms, roots)?
✓ Use the math module
Financial calculations or needing decimal precision control?
✓ Use the decimal module -- always create from a string, not a float
Need exact fraction results (1/3, 7/8)?
✓ Use the fractions module
Basic statistical analysis (mean, median, stdev)?
✓ Use the statistics module
Need to compare two float values?
✓ Use math.isclose(), not == directly
Summary #
- Floats aren’t always exact —
0.1 + 0.2 != 0.3is a property of IEEE 754, not a bug. Usemath.isclose()to compare floats, not==.- The
mathmodule for general mathematical operations: rounding (ceil,floor,trunc), roots and powers (sqrt,pow,isqrt), logarithms (log,log2,log10), trigonometry, and combinatorics (comb,perm,factorial).- Trigonometric functions work in radians — use
math.radians()andmath.degrees()for conversion.math.atan2(y, x)is safer thanmath.atan(y/x)because it handles all quadrants correctly.- The
decimalmodule for financial calculations — always create from a string (Decimal("0.1")), not from a float (Decimal(0.1)).- The
fractionsmodule for exact fractional arithmetic with no rounding errors at all.- The
statisticsmodule for basic data analysis without external dependencies; consider NumPy or pandas for bigger needs.