Checker Approximation   as of Julia version 2.4 (built on 23 Oct 2017)

belongs to group Basic

Identify operations that might be incorrect because of numerical approximations


Floating point operations are by nature approximated. This can introduce bugs when precise, mathematical properties are expected from inherently imprecise floating point computations. Moreover, computations that might lose precision or overflow, whose result is stored in a larger type, are suspicious since, by computing on the larger type from the beginning, one could avoid approximations and overflows.

Action: Use approximated comparisons (up to some epsilon) rather than exact comparisons. Use integral numbers instead of floating point values, whenever possible. Compute over integral numbers and translate into floating points only at the end, whenever possible. If a large type for the result of a computation is desired, compute from the beginning on that type instead of applying a type conversion at the end of the computation.

Examples


Consider the following program:

public class Approximation {

  public static void main(String[] args) {
    for (double d = 0.0; d != 5.0; d += 0.001)
      System.out.println(d);
  }
}

This checker issues the following warning:

Approximation.java:4: [Approximation: FloatComparisonWarning] Unsafe comparison between non-integral numbers, that might be the approximate result of the computation at line 4

since the comparison between d and 5.0 will always yield true, because of approximations in the computation d += 0.001 at line 4. The while loop will actually never terminate.

In this example, the programmer should replace d != 5.0 with d < 5.0 or replace floating point computations with (stable and more efficient) integral computations, as follows:

for (int d = 0; d != 5000; d++)
  System.out.println(d / 1000.0);

Consider the following program now:

public class MillisecondsInDays {
  public static void main(String[] args) {
    int days = Integer.parseInt(args[0]);
    long milliseconds = days * 24 * 60 * 60 * 1000;

    System.out.printf("Inside %d days there are %d milliseconds", days, milliseconds);
  }
}

This checker will issue the following warning:

MillisecondsInDays.java:4: [Approximation: CastIntComputationIntoLong] Cast of possibly overflowing int computation into long: this might lose precision

since the computation at line 4 is performed on integers and only at the end its result is stored into a long. As a consequence, the extra space allowed by a long value is not exploited and the computation would overflow if days is larger than 24. In this case, the programmer should have written instead:

public class MillisecondsInDays {
  public static void main(String[] args) {
    int days = Integer.parseInt(args[0]);
    long milliseconds = days * 24L * 60 * 60 * 1000;

    System.out.printf("Inside %d days there are %d milliseconds", days, milliseconds);
  }
}

so that the whole computation is performed with long values and the whole space of a long is exploitable. Overflow will only occur for very large values of days.