All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.l10n.py.rules.python.S1244.html Maven / Gradle / Ivy

There is a newer version: 4.23.0.17664
Show newest version

This rule raises an issue when direct and indirect equality/inequality checks are made on floats.

Why is this an issue?

Floating point math is imprecise because of the challenges of storing such values in a binary representation.

In base 10, the fraction 1/3 is represented as 0.333…​ which, for a given number of significant digit, will never exactly be 1/3. The same problem happens when trying to represent 1/10 in base 2, with leads to the infinitely repeating fraction 0.0001100110011…​. This makes floating point representations inherently imprecise.

Even worse, floating point math is not associative; push a float through a series of simple mathematical operations and the answer will be different based on the order of those operation because of the rounding that takes place at each step.

Even simple floating point assignments are not simple, as can be vizualized using the format function to check for significant digits:

>>> format(0.1, ".17g")
'0.10000000000000001'

This can also be vizualized as a fraction using the as_integer_ratio method:

>>> my_float = 0.1
>>> numerator, denominator = my_float.as_integer_ratio()
>>> f"{numerator} / {denominator}"
'3602879701896397 / 36028797018963968'

Therefore, the use of the equality (==) and inequality (!=) operators on float values is almost always erroneous.

How to fix it

Whenever attempting to compare float values, it is important to consider the inherent imprecision of floating-point arithmetic.

One common solution to this problem is to use 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. Note that comparing a value to 0 is a special case: as it has no magnitude, it is important to use an absolute tolerance value.

The math.isclose function allows to compare floats with a relative and absolute tolerance. One should however be careful when comparing values to 0, as by default, the absolute tolerance of math.isclose is 0.0 (this case is covered by rule {rule:python:S6727}) . Depending on the library you’re using, equivalent functions exist, with possibly different default tolerances (e.g numpy.isclose or torch.isclose which are respectively designed to work with numpy arrays and pytorch tensors).

If precise decimal arithmetic is needed, another option is to use the Decimal class of the decimal module, which allows for exact decimal arithmetic.

Code examples

Noncompliant code example

def foo(a, b):
    return a == b - 0.1

Compliant solution

import math
def foo(a, b):
    return math.isclose(a, b - 0.1, rel_tol=1e-09, abs_tol=1e-09)

Resources

Documentation

Related rules

  • {rule:python:S6727} - The abs_tol parameter should be provided when using math.isclose to compare values to 0




© 2015 - 2024 Weber Informatics LLC | Privacy Policy