0

I've just been through a bug where two number were being compared and I found the following interesting:

    assert 1 == 1;//true        
    assert 1d == 1;//true        
    assert 1 == 1f;//true        
    assert 1d == 1f;//true
    assert 1.1 == 1.1;//true        
    assert 1.1d == 1.1;//true        
    assert 1.1 == 1.1f;//false        
    assert 1.1d == 1.1f;//false

My question is: Why only the two last statements are false?

Androiderson
  • 16,865
  • 6
  • 62
  • 72
  • Very good explanations : https://randomascii.wordpress.com/2012/06/26/doubles-are-not-floats-so-dont-compare-them/ – Alexandre Cartapanis Jan 20 '16 at 18:01
  • 1
    You MUST read [What Every Computer Scientist Should Know About Floating-Point Arithmetic](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html). It is the canonical resource for this. – Jim Garrison Jan 20 '16 at 18:02
  • what's the point to check equality for two different types values...never mind.. – Haifeng Zhang Jan 20 '16 at 18:05
  • I would suggest reading http://floating-point-gui.de first, as it's more approachable and gets you introduced to the concepts before you get math'ed. – yshavit Jan 20 '16 at 18:05

3 Answers3

13

It's because of rounding.

1 can be represented exactly as a double or a float so the first 4 comparisons work as expected.

1.1 == 1.1 compares a double to itself and works as expected.

1.1d == 1.1 is exactly the same as above (the d is implied in 1.1).

The last two comparisons compare 1.1 the double to 1.1 the float. Except that 1.1 is not exactly representable as a float (resp. double) so it gets rounded to the closest float (resp. double) - but double has a "higher resolution" so they don't get rounded similarly.

To see the exact values:

System.out.println(new BigDecimal(1.1f)); //1.10000002384185791015625
System.out.println(new BigDecimal(1.1d)); //1.100000000000000088817841970012523233890533447265625

As suggested by @yshavit in the comments, when you compare a double to a float, the float gets converted to a double (due to numeric promotion), so you are really comparing two doubles:

System.out.println(new BigDecimal((double) 1.1f)); //1.10000002384185791015625
System.out.println(new BigDecimal(1.1d)); //1.100000000000000088817841970012523233890533447265625
assylias
  • 321,522
  • 82
  • 660
  • 783
  • 1
    I think for bonus points, `System.out.println(new BigDecimal((double) 1.1f))` -- to show how the rounded float gets widened to a double. – yshavit Jan 20 '16 at 18:03
2

Launch this program

public static void main(String... args) {
    System.out.println("1.1f bits: " + Long.toBinaryString(Double.doubleToLongBits(1.1f)));
    System.out.println("1.1d bits: " + Long.toBinaryString(Double.doubleToLongBits(1.1d)));
}

it yelds

1.1f as double: 11111111110001100110011001100110100000000000000000000000000000
1.1d as double: 11111111110001100110011001100110011001100110011001100110011010

The reason for this behavior is that the literal 1.1, ie the decimal fraction 1.1dec does not have a finite representation as a binary fraction, and IEEE-754 stores numbers as binary fractions. So it is rounded and the result depends on the number of bits available at conversion time (by the way, the conversion does not even happen at runtime - it's a compiler's job).

When operators of different size are compared, the smaller is promoted. Note that it's not the case that double and float can't be equal:

3.1f != 3.1d
3.0f == 3.0d
Raffaele
  • 20,627
  • 6
  • 47
  • 86
1

The number 1.1 cannot be represented exactly in binary floating point representation, thus slightly differ in the 4 byte float and 8 byte double representation.

Therefore the two last are false, because they compare a double with a float for a number which cannot exactly be representetd.
The other just compares a double with a double or use a value which can exactly be represented like 1.0

AlexWien
  • 28,470
  • 6
  • 53
  • 83