7
System.out.println(2.00-1.10)

and

System.out.println((double)(2.00-1.10)) 

both output same result 0.8999999999999999, but

System.out.println((float)(2.00-1.10)) 

outputs 0.9.

Perhaps by default Java performs calculations in double, then why does downcasting corrects result?

And if 1.1 is converted to 1.100000000000001 in double then why does

System.out.println((double)(1.10)) outputs 1.1 only.

EDIT: To get why this happens we need to understand both answers. First the canonical representation is actually different at lower level. Next how the return value of toString is changed/rounded off/matched with the nearest double of argument.

Sparsh Singhal
  • 127
  • 1
  • 5
  • 2
    read [this](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) – ΦXocę 웃 Пepeúpa ツ May 30 '17 at 14:42
  • "Perhaps by default Java performs calculations in double" kind of, since `2.00` and `1.10` by default are considered doubles (unless you explicitly state that they should be floats, like by adding `f` suffix) so result of `double+double = double` which explains why casting `(double)(2.00-1.10)` doesn't change anything. – Pshemo May 30 '17 at 14:46
  • @biziclop, although the alleged duplicate is related to this question; there is variation in this version of the issue. The so called duplicate would not explain the most fundamental aspect of this question: why the downcasting fixes the problem? – Edwin Dalorzo May 30 '17 at 14:48
  • 1
    @EdwinDalorzo Thanks for reopening the question. I think the size of mantisa only decides the range. And the reason is well explained in answers below. – Sparsh Singhal May 30 '17 at 18:51

3 Answers3

8

0.9 is not representable as a double or a float. The internal details of floating point calculation has been answered in various posts on SO, such as: Is floating point math broken?.

In your specific example, you can see the double and float that are closest to 0.9 with this code:

System.out.println(new BigDecimal(0.9d));
System.out.println(new BigDecimal(0.9f));

which outputs the canonical double and float representations of 0.9:

0.90000000000000002220446049250313080847263336181640625
0.89999997615814208984375

Now when you calculate 2.0 - 1.1, the result is:

System.out.println(new BigDecimal(2.0-1.1));

0.899999999999999911182158029987476766109466552734375

You can see that it is not the canonical representation of 0.9 hence you get a different result.

However float precision is not as good and:

System.out.println(new BigDecimal((float) (2.0-1.1)));

0.89999997615814208984375

returns the same number as the canonical representation of 0.9f.

assylias
  • 321,522
  • 82
  • 660
  • 783
  • Just a comment: `BigDecimal` has no constructor that accepts `float` argument. Java just casting it back into a `double`... – Usagi Miyamoto May 30 '17 at 15:18
  • 1
    @UsagiMiyamoto yes but it doesn't matter, when you cast a float to a double you get the same number. – assylias May 30 '17 at 15:22
  • @assylias We get different results, understood. Then is it `println` method which does the rounding off to `0.9`? – Sparsh Singhal May 30 '17 at 17:42
  • 1
    @SparshSinghal not the println, but the `String.valueOf` method, which in turn calls the `Double.toString` method (the static one). Have a look at the javadoc for more info: https://docs.oracle.com/javase/8/docs/api/java/lang/Double.html#toString-double- – assylias May 30 '17 at 17:45
4

It's not particularly well-explained by the documentation, but Double.toString(double) essentially performs some rounding in the output it produces. The Double.toString algorithm is used all throughout Java SE, including e.g. PrintStream.println(double) of System.out. The documentation says this:

How many digits must be printed for the fractional part of m or a? There must be at least one digit to represent the fractional part, and beyond that as many, but only as many, more digits as are needed to uniquely distinguish the argument value from adjacent values of type double. That is, suppose that x is the exact mathematical value represented by the decimal representation produced by this method for a finite nonzero argument d. Then d must be the double value nearest to x; or if two double values are equally close to x, then d must be one of them and the least significant bit of the significand of d must be 0.

In other words, it says that the return value of toString is not necessarily the exact decimal representation of the argument. The only guarantee is that (roughly speaking) the argument is closer to the return value than any other double value.

So when you do something like System.out.println(1.10) and 1.10 is printed, this does not mean the value passed in is actually equivalent to the base 10 value 1.10. Rather, essentially the following happens:

  • First, during compilation, the literal 1.10 is examined and rounded to produce the nearest double value. (It says in the JLS here the rules for this are e.g. detailed in Double.valueOf(String) for double.)
  • Second, when the program runs, Double.toString produces a String representation of some decimal value which the double value produced in the previous step is closer to than any other double value.

It just so happens that conversion to String in the second step often produces a String which is the same as the literal in the first step. I'd assume this is by design. Anyhow though, the literal e.g. 1.10 does not produce a double value which is exactly equal to 1.10.

You can discover the actual value of a double (or float, because they can always fit in a double) using the BigDecimal(double) constructor:

When a double must be used as a source for a BigDecimal, note that this constructor provides an exact conversion; it does not give the same result as converting the double to a String using the Double.toString(double) method and then using the BigDecimal(String) constructor. To get that result, use the static valueOf(double) method.

// 0.899999999999999911182158029987476766109466552734375
System.out.println(new BigDecimal((double) ( 2.00 - 1.10 )));
// 0.89999997615814208984375
System.out.println(new BigDecimal((float)  ( 2.00 - 1.10 )));

You can see that neither result is actually 0.9. It's more or less just a coincidence that Float.toString happens to produce 0.9 in that case and Double.toString does not.


As a side note, (double) (2.00 - 1.10) is a redundant cast. 2.00 and 1.10 are already double literals so the result of evaluating the expression is already double. Also, to subtract float, you need to cast both operands like (float) 2.00 - (float) 1.10 or use float literals like 2.00f - 1.10f. (float) (2.00 - 1.10) only casts the result to float.

Radiodef
  • 37,180
  • 14
  • 90
  • 125
  • Those redundant casts were to check if they made any difference. None, I suppose. – Sparsh Singhal May 30 '17 at 17:52
  • Yes, there's no difference. Your reasoning for doing it is perfectly fine, though. The relevant specification is: *"A floating-point literal is of type `float` if it is suffixed with an ASCII letter `F` or `f`; otherwise its type is `double` and it can optionally be suffixed with an ASCII letter `D` or `d`*". (Which is from [here](https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.10.2), immediately following the two gray boxes which describe the grammar of a floating-point literal.) – Radiodef May 30 '17 at 17:57
1

You are confusing the actual value of the variable with how the value is displayed on the screen. In your example, the toString method is used to convert the values to a String before they are displayed. This uses a default for how many places to use (which is different for double and float). Below I explicitly set how many places to display:

double d1 = 2 - 1.1;
float f1 = (float) d1;

System.out.println( String.format( "d1 = %.10f f1 = %.10f", d1, f1));
System.out.println( String.format( "d1 = %.20f f1 = %.20f", d1, f1));
System.out.println( String.format( "Using toString, d1 = %s f1 = %s", "" + d1, "" + f1));

gives the output:

d1 = 0.9000000000 f1 = 0.8999999762
d1 = 0.89999999999999990000 f1 = 0.89999997615814210000
Using toString, d1 = 0.8999999999999999 f1 = 0.9

This shows that the float value is less accurate that the double one.

pcarter
  • 1,516
  • 14
  • 21
  • I think `toString` rounds off the `float` value but why not of `double`? – Sparsh Singhal May 30 '17 at 17:34
  • 1
    It rounds off both, but to different numbers of significant digits. Since a `float` is less accurate, it rounds it off more. It's just an accident of the particular values that the result look different for this example. Floating point values are almost always approximations. The values 0.8999999999999999 and 0.9 may look different, but they are _very_ close to the same value. For almost all practical purposes, they are the same value. If it's important what the string representation looks like, you should use a format that rounds to the appropriate significant digits. – pcarter May 30 '17 at 18:00