Comparing float values for equality directly is not reliable and should be avoided, due to the inherent imprecision in the binary representation of
floating point numbers. Such comparison is reported by S1244.
One common solution to this problem is to use the math.isclose
function to perform the comparison. Behind the scenes, the
math.isclose
function uses a tolerance value (also called epsilon) to define an acceptable range of difference between two floats. A
tolerance value may be relative (based on the magnitude of the numbers being compared) or absolute.
Using a relative tolerance would be equivalent to:
def isclose_relative(a, b, rel_tol=1e-09):
diff = abs(a - b)
max_diff = rel_tol * max(abs(a), abs(b))
return diff <= max_diff
Using an absolute tolerance is equivalent to:
def isclose_absolute(a, b, abs_tol=1e-09):
diff = abs(a - b)
return diff <= abs_tol
The math.isclose
method uses both relative and absolute tolerances and can be approximated as:
def is_close(a, b, rel_tol=1e-09, abs_tol=0.0):
diff = abs(a - b)
max_diff = max(rel_tol * max(abs(a), abs(b)), abs_tol)
return diff <= max_diff
Whenever comparing values that are close to 0, the value of the relative tolerance may be too small to overcome the imprecision introduced by
floating-point arithmetic. It is therefore important to rely on an absolute tolerance in this case.
When using math.isclose
, the absolute tolerance is defined through the parameter abs_tol
. By default, the value of this
parameter is 0.0
. Therefore, using math.isclose
to compare values against zero without providing this parameter is
equivalent to a strict equality check, which is likely not intended.
Exceptions
Note that similar methods from different libraries may behave differently. Notably, numpy.isclose
has a default absolute tolerance of
1e-08
. No issue will be reported in this case. However, to ensure consistency and intentionality, it is recommended to always set the
tolerance values.