When handling decimal numbers in computers, they are represented in binary internally, which inevitably causes minute “calculation errors.” Therefore, comparing floating-point numbers (float) using the == operator may not yield the expected result.
To solve this problem, Python 3.5 introduced the isclose() function in the standard math library.
This article explains the problems that occur when comparing floating-point numbers and the correct method using math.isclose().
The Problem with Floating-Point Comparison
First, let’s try comparing the result of a simple addition. 0.1 + 0.2 should be 0.3, but in Python (and many other programming languages), it evaluates to False.
# Calculate 0.1 + 0.2
calc_result = 0.1 + 0.2
expected_value = 0.3
print(f"Calculation Result: {calc_result}")
print(f"Expected Value: {expected_value}")
# Compare with equality operator (==)
is_equal = (calc_result == expected_value)
print(f"Strict Comparison (==): {is_equal}")
Execution Result:
Calculation Result: 0.30000000000000004
Expected Value: 0.3
Strict Comparison (==): False
The calculation result becomes 0.30000000000000004, which is slightly different from 0.3, so the equality check returns False. This is the problem caused by floating-point errors.
Solution with math.isclose()
The math.isclose() function determines if two values are “close enough (within a tolerance).” This allows you to treat them as “practically equal” by accepting minute errors.
Syntax:
import math
math.isclose(a, b, rel_tol=1e-09, abs_tol=0.0)
Basic Usage
Let’s rewrite the previous example using math.isclose().
import math
val_a = 0.1 + 0.2
val_b = 0.3
# Compare with isclose
is_close = math.isclose(val_a, val_b)
print(f"Comparison with math.isclose: {is_close}")
Execution Result:
Comparison with math.isclose: True
Even with the default settings, it absorbs general calculation errors and returns True (equal).
Setting Tolerance
You can finely control how much error math.isclose() allows using arguments.
1. Relative Tolerance (rel_tol)
rel_tol (relative tolerance) specifies the allowed error as a ratio (like a percentage) of the input value’s magnitude. The default is 1e-09 (1 in 1 billion).
import math
target = 100.0
# Value with an error of 0.1 relative to 100.0
value = 100.1
# Setting to allow 1% (0.01) error
is_close_loose = math.isclose(value, target, rel_tol=0.01)
# Setting to allow only 0.01% (0.0001) error
is_close_strict = math.isclose(value, target, rel_tol=0.0001)
print(f"1% Tolerance: {is_close_loose}") # True
print(f"0.01% Tolerance: {is_close_strict}") # False
2. Absolute Tolerance (abs_tol)
abs_tol (absolute tolerance) allows a fixed numerical difference regardless of the value’s magnitude. This is effective when comparing very small numbers close to zero, where the default relative tolerance defaults to 0.
import math
near_zero_a = 0.00001
near_zero_b = 0.00002
# Since relative tolerance might fail near zero,
# set absolute tolerance to allow differences within 0.0001
is_close_zero = math.isclose(near_zero_a, near_zero_b, abs_tol=0.0001)
print(f"Comparison near zero: {is_close_zero}")
Execution Result:
Comparison near zero: True
Summary
- Using
==for floating-point numbers (float) may lead to unintended results due to calculation errors. - Using
math.isclose(a, b)enables correct comparison by considering errors. - Specify
rel_tol(relative tolerance) orabs_tol(absolute tolerance) to adjust the acceptable range as needed. - Using
math.isclose()is recommended in programs involving numerical calculations to prevent bugs.
