PEP 485: A Function for Testing Approximate Equality
TL;DR
PEP 485 introduced math.isclose()
to solve the fundamental problem of comparing floating-point numbers by testing if two values are “approximately equal” rather than exactly equal.
Interesting!
The default relative tolerance (1e-9) was carefully chosen to be about half of float precision, and the absolute tolerance defaults to 0.0 to force developers to consciously think about appropriate tolerance values for their specific use case.
The Floating-Point Dilemma
python code snippet start
import math
# The classic floating-point problem
print(0.1 + 0.2 == 0.3) # False - surprise!
print(0.1 + 0.2) # 0.30000000000000004
# This is where math.isclose() shines
print(math.isclose(0.1 + 0.2, 0.3)) # True
python code snippet end
Before PEP 485, developers had to roll their own comparison functions, leading to inconsistent and often incorrect implementations.
The Algorithm Behind the Magic
python code snippet start
# The isclose() algorithm (simplified):
# abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
import math
def demonstrate_algorithm():
a, b = 1.0000001, 1.0000002
rel_tol = 1e-9
abs_tol = 0.0
diff = abs(a - b)
relative_tolerance = rel_tol * max(abs(a), abs(b))
threshold = max(relative_tolerance, abs_tol)
print(f"Difference: {diff}")
print(f"Threshold: {threshold}")
print(f"Close enough: {diff <= threshold}")
print(f"math.isclose(): {math.isclose(a, b)}")
demonstrate_algorithm()
python code snippet end
Practical Testing Scenarios
python code snippet start
import math
# Testing computational results
def test_square_root():
x = 2.0
result = math.sqrt(x) ** 2
expected = 2.0
# This might fail due to floating-point precision
assert result == expected # Risky!
# This is robust
assert math.isclose(result, expected) # Safe!
# Iterative algorithm convergence
def check_convergence(current, previous, tolerance=1e-6):
return math.isclose(current, previous, rel_tol=tolerance)
# Different tolerance strategies
values = [1.000001, 1.000002]
print(f"Default: {math.isclose(*values)}") # False
print(f"Loose: {math.isclose(*values, rel_tol=1e-5)}") # True
print(f"With abs_tol: {math.isclose(*values, abs_tol=1e-5)}") # True
python code snippet end
Working with Different Numeric Types
python code snippet start
from decimal import Decimal
from fractions import Fraction
# Works across numeric types
print(math.isclose(1.0, Decimal('1.0'))) # True
print(math.isclose(0.5, Fraction(1, 2))) # True
# Handles edge cases gracefully
print(math.isclose(float('inf'), float('inf'))) # True
print(math.isclose(float('nan'), float('nan'))) # False (by design)
python code snippet end
Design Philosophy
The symmetric comparison method ensures that isclose(a, b)
always equals isclose(b, a)
, making it predictable and mathematically sound. The function handles IEEE 754 special values (infinity, NaN) according to established standards.
PEP 485 solved a fundamental pain point in numerical computing by providing a standard, well-designed solution that works consistently across different scenarios and numeric types.
math module functions precise decimal arithmetic
Reference: PEP 485 – A Function for testing approximate equality