0

It's know that floating point number, even those with fixed digits after decimal point in decimal format, can't be represented exactly. So I have the following program to test:

public class Main {
  public static void main(String[] args) {
    System.out.printf("0.1 in single precision is %.50f\n", 0.1f);
    System.out.printf("0.2 in single precision is %.50f\n", 0.2f);
    System.out.printf("0.3 in single precision is %.50f\n", 0.3f);
    System.out.printf("0.1 + 0.2 in single precision is %.50f\n", 0.1f + 0.2f);
    System.out.printf("0.1 + 0.2 == 0.3 is %b in single precision\n", 0.1123f * 0.4f + 0.2f * 0.5f == 0.2f * 0.7f + 0.0123f * 0.4f);

    System.out.println();

    System.out.printf("0.1 in double precision is %.50f\n", 0.1);
    System.out.printf("0.2 in double precision is %.50f\n", 0.2);
    System.out.printf("0.3 in double precision is %.50f\n", 0.3);
    System.out.printf("0.1 + 0.2 in double precision is %.50f\n", 0.1 + 0.2);
    System.out.printf("0.1 + 0.2 == 0.3 is %b in double precision\n", 0.1 + 0.2 == 0.3);
  }
}

The output is the following:

0.1 in single precision is 0.10000000149011612000000000000000000000000000000000
0.2 in single precision is 0.20000000298023224000000000000000000000000000000000
0.3 in single precision is 0.30000001192092896000000000000000000000000000000000
0.1 + 0.2 in single precision is 0.30000001192092896000000000000000000000000000000000
0.1 + 0.2 == 0.3 is true in single precision

0.1 in double precision is 0.10000000000000000000000000000000000000000000000000
0.2 in double precision is 0.20000000000000000000000000000000000000000000000000
0.3 in double precision is 0.30000000000000000000000000000000000000000000000000
0.1 + 0.2 in double precision is 0.30000000000000004000000000000000000000000000000000
0.1 + 0.2 == 0.3 is false in double precision

Two questions I can't answer from the above result and I am seeking help for:

  1. Why does the double representation of 0.1, 0.2 and 0.3 looks exact, whereas 0.1 + 0.2 doesn't.
  2. Why does 0.1f + 0.2f == 0.3f return true?
user690421
  • 422
  • 1
  • 5
  • 15
  • It is usually not a good idea to compare floating point numbers (in any language). You can subtract them, and see if the result is less or equal to a certain very small number. Please see http://stackoverflow.com/questions/322749/retain-precision-with-double-in-java and http://floating-point-gui.de/languages/java/ – TR1 Nov 20 '15 at 17:25
  • 2
    Possible duplicate of [Is floating point math broken?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken) – JFPicard Nov 20 '15 at 17:26
  • 1
    @JFPicard this doesn't seem to be a duplicate; the OP seems to be aware of that issue and is trying to understand it more deeply. – Louis Wasserman Nov 20 '15 at 17:27

1 Answers1

3
  1. I am suspicious of System.out.printf working correctly here. A reliable way to get the exact double value you get when you write 0.1 is to write new BigDecimal(0.1).toString().
  2. "Why does 0.1f + 0.2f == 0.3f return true?" Pretty much because you just got lucky: rounding 0.1 to the closest float representation and 0.2 to the closest float representation and adding them gets you the closest representable float to 0.3. That's not true in general, those values just happen to work.
Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • For question (2). I also suspected that 0.1f + 0.2f == 0.3f return true is coincidental. However, 0.1 + 0.2 == 0.3 returns false. I can't explain why single is "getting luck" but double is not in this case. Maybe because the extra significant digits of double works against it in this case? – user690421 Nov 20 '15 at 18:05
  • Yep, pretty much. More digits mean it's harder to get lucky that different roundings work out to match. – Louis Wasserman Nov 20 '15 at 18:06
  • The printf output of 0.1000... for the double case is correct, at least in so far as that it agrees with Double.toString(0.1). The reason it displays zeros after the 17 significant digits is that toString cuts of output at a point where parsing the decimal will lead back to the original double number unambigously. So yes, it rounds the output string, but that is defined to *be* correct, see javadoc of Double.toString for some info. – Durandal Nov 20 '15 at 20:33
  • @Durandal it's correct in that it reflects `Double.toString`, but I believe the OP's goal was to extract the true numerical value of the `double` represented by 0.1. – Louis Wasserman Nov 20 '15 at 20:34
  • Agreed, that was probably his idea. But thats hardly a justifiable basis for judging correctness. Correctness needs to be judged against some kind of specification, not someones intent or intuition. While the definition is extremely roundabout, if you follow the javadoc references given for printf, you will arrive (after quite a few hops) at Double.toString. Sneakily hidden, but it is specified. – Durandal Nov 20 '15 at 20:48