Check If the Floating Point Numbers Are Close in Python: math.isclose
Floating point numbers, float
, are represented in binary inside computers, so they cannot represent exactly the same values as decimal numbers. This can lead to unexpected results when comparing floating point numbers.
See the following article about NumPy functions np.isclose()
and np.allclose()
.
Notes on comparing floating point numbers
Floating point numbers, float
, are represented in binary inside computers, so they cannot represent exactly the same values as decimal numbers.
For example, print(0.1)
outputs 0.1
. This output, however, is an approximation. Python rounds the actual binary representation of 0.1
to a manageable number of decimal places for display. If you increase the number of digits with format()
, you may find that it actually contains an error.
print(0.1)
# 0.1
print(format(0.1, '.20f'))
# 0.10000000000000000555
See the official documentation for details.
In many cases, there is no need to be concerned about such errors. However, they can lead to unexpected results, especially when comparing the results of operations with floating point numbers.
print(0.1 + 0.1 + 0.1)
# 0.30000000000000004
print(0.1 + 0.1 + 0.1 == 0.3)
# False
print((19 / 155) * (155 / 19))
# 0.9999999999999999
print((19 / 155) * (155 / 19) == 1)
# False
For example, you can use round()
to round, or abs()
to compare the absolute value of the difference with a sufficiently small value.
- Round numbers with round() and Decimal.quantize() in Python
- Get the absolute value with abs() and math.fabs() in Python
print(round(0.1 + 0.1 + 0.1, 10) == round(0.3, 10))
# True
print(abs((0.1 + 0.1 + 0.1) - 0.3) < 1e-10)
# True
In Python, 1e<number>
is a way to express floating point numbers in scientific notation. Here, 1e<number>
represents 10
to the power of <number>
. It's important to note that the e
used here is a symbol for exponentiation, not Euler's number.
print(1e5)
# 100000.0
print(1e-3)
# 0.001
Check if the two floating point numbers are close: math.isclose()
You can write a comparison of floating point numbers like examples above simply by using the function isclose()
in the math
module.
As shown below, math.isclose(a, b)
returns True
if a
and b
are approximately equal.
import math
print(math.isclose(0.1 + 0.1 + 0.1, 0.3))
# True
print(math.isclose((19 / 155) * (155 / 19), 1))
# True
Specify tolerances: rel_tol
, abs_tol
You can specify the tolerable difference with the rel_tol
and abs_tol
arguments. By default, rel_tol
is set to 1e-9
, and abs_tol
is set to 0.0
.
rel_tol
is the relative tolerance, and it is the maximum allowed difference between two values, relative to the larger absolute value.
abs(a - b) <= rel_tol * max(abs(a), abs(b))
print(math.isclose(1, 1.001))
# False
print(math.isclose(1, 1.001, rel_tol=0.01))
# True
abs_tol
is the minimum absolute tolerance.
abs(a - b) <= abs_tol
It is compared to the larger of the relative and absolute tolerances.
abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
Be careful when comparing a number with 0
. For example, if b=0
, the comparison math.isclose(a, b)
will not return True
unless rel_tol=0
. This is because the comparison is evaluated as follows:
abs(a) <= rel_tol * abs(a)
Remember to specify abs_tol
when needed, as it defaults to 0.0
.
print(math.isclose(0, 0.001))
# False
print(math.isclose(0, 0.001, rel_tol=0.01))
# False
print(math.isclose(0, 0.001, abs_tol=0.01))
# True
Compare floating point number with 0
As mentioned above, be careful when comparing the result of a floating point operation to 0
. Using ==
or math.isclose()
by default may produce unexpected results.
The same is true for operations dealing with irrational numbers, such as pi.
print(math.sin(math.pi))
# 1.2246467991473532e-16
print(math.sin(math.pi) == 0)
# False
print(math.isclose(math.sin(math.pi), 0))
# False
You can specify abs_tol
in math.isclose()
, round with round()
, or compare with a sufficiently small value instead of 0
.
print(math.isclose(math.sin(math.pi), 0, abs_tol=1e-10))
# True
print(round(math.sin(math.pi), 10) == 0)
# True
print(abs(math.sin(math.pi)) < 1e-10)
# True