0

What is precision of double/float comparison (relation operator) with integral types?

I understand that it is not recommended to use floating-point in any comparisons. But so far that this operation is allowed, here is the question:

    int x = 90;
    float y = 90.00_001f; 

    if (x < y) {
        System.out.println("it works!"); // gets printed
    }

    // now just add one zero to lessen the precision a bit...

    int x = 90;
    float y = 90.000_001f;

   if (x < y) {
      System.out.println("it works!"); // not printed
   }
Code Complete
  • 3,146
  • 1
  • 15
  • 38

3 Answers3

3

The problem here is not with the comparison to integer, but with the precision of float itself.

You can specify as many decimal places as you like when you specify a float (or double) literal; but this doesn't mean that the precision will be retained when the code is compiled.

For example, all of the following have the same value:

90.f
90.000_001f
90.000000000000000000000000000000000000000000000000000000000‌​00000001f

Ideone demo

So the reason the second comparison fails is that 90.000_001f is equal to 90.f; and that's equal to the value of widening 90 to a float.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
0

The precision of a IEEE 754 single-precision floating point is between 6 and 9 significant decimal digits (source).

For double-precision you get between 15 and 17 significant decimal digits.

lexicore
  • 42,748
  • 17
  • 132
  • 221
0

The other answers are correct, so I won't repeat them here.

What I want to add is another potential surprise about considering == as an equivalence relation, which normally imply associativity:

(a == b) and (b == c) => (a == c)

But if you try this snippet for example in https://repl.it :

class Main {
  public static void main(String[] args) {
    int i=16777217;
    float f=16777216.0f;
    double d=16777216.0;
    if(i == f) {
       System.out.println("i == f");
    }
    if(d == f) {
       System.out.println("d == f");
    }
    if(i == d) {
       System.out.println("i == d");
    }
  }
}

The surprise is that i==f and f==d but not(i==d)...
This is because when writing i==f, an implicit conversion float(i)==f happens, and this conversion potentially loose precision because an int can require up to 31 bits of precision, while a float offers at most 24.

This could have been different, if you look how Lisp, Scheme or recent Squeak/Pharo Smalltalk handles the comparisons, you'll see that they care for exactness...

Extract from http://www.lispworks.com/documentation/lcl50/aug/aug-170.html

In general, when an operation involves both a rational and a floating-point argument, the rational number is first converted to floating-point format, and then the operation is performed. This conversion process is called floating-point contagion. However, for numerical equality comparisons, the arguments are compared using rational arithmetic to ensure transitivity of the equality (or inequality) relation.

aka.nice
  • 9,100
  • 1
  • 28
  • 40