5

The following code uses the values of type float and double respectively.

float a=99999.99F;
double b=a;

System.out.println("a : "+a);
System.out.println("b : "+b);
System.out.println("a==b : "+(a==b));

It displays the following output:

a : 99999.99
b : 99999.9921875
a==b : true

After execution, the values of a and b are 99999.99 and 99999.9921875 respectively though a==b returns true.

How does the comparison expression a==b return true?

Tiny
  • 27,221
  • 105
  • 339
  • 599
  • 4
    floats are not safe to be directly compared they in any language must be compared against range > & < epsilon to be more precise – amar Dec 19 '13 at 13:36
  • This answer is also worth looking at: [link] (http://stackoverflow.com/a/3730249/248847) – KrishPrabakar Dec 19 '13 at 14:03
  • 1
    @amar: That is not the issue here, and it is not generally applicable advice. It is a myth that comes out of a lack of understand of how floating-point operations work. – Eric Postpischil Dec 19 '13 at 15:06
  • 4
    @PhilippSander: This question is not a duplicate of that question because, in this question, the numbers being compared are exactly equal, and the `==` operator returns the correct result. – Eric Postpischil Dec 19 '13 at 15:07
  • 1
    See also: http://stackoverflow.com/questions/259015/can-every-float-be-expressed-exactly-as-a-double – Pascal Cuoq Dec 19 '13 at 15:23

4 Answers4

7

On the final line, where you compare a==b, the value of a is being implicitly promoted to double before the comparison. So, in effect, what is really being evaluated is (((double)a)==b) which, of course, evaluates true, since b was initialized by casting a as double in the first place.

In other words, the last boolean expression (a==b) asks:

is (some float value converted to a double)==(the same float value converted to a double somewhere else)

and the answer is : yes.

UPDATE: a commenter below made a good point: when a is promoted to double, the stored numeric value does not actually change, even though it appears to because a different value is printed. The issue is with how floating point numbers are stored; without making this answer WAAAY too long and wading in past my depth, what you need to know is that some simple decimal values cannot be perfectly represented in binary floating point form, no matter how many digits you have (just like you can't perfectly represent 1/3 in base 10 without an infinite number of 3s, as in 0.333333...), so when you assign one of these values using float (a 32-bit representation of a floating point number) and then you convert it to a double (a 64-bit representation) and then use println to display the value of that number, you may see more digits.

The println method displays the shortest string of decimal digits that convert to the same float (or double) value the computer sees internally. Although the same numerical value is passed as argument in the first two calls to println in your example, the double type has more precision so println needs to print more digits to show “the shortest string of decimal digits that would convert to (double)99999.99F”. If your example had been 9999.50, both numbers would have printed the same, because binary numbers can represent 1/2 just fine.

NOTE: I am not even close to an expert on floating point numbers; you should check out (http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) if you want a deeper understanding of what is going on here.

Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
JVMATL
  • 2,064
  • 15
  • 25
  • 1
    Your explanation is good but I wish your answer said that converting a `float` value to `double` does not change the value. Every `float` value can be represented as a `double`, and upon conversion, the `double` that is picked is the `double` that represents the same value. The behavior that surprises the OP is in Java's printing function (but not in the conversion to `double` and not in `==`). – Pascal Cuoq Dec 19 '13 at 15:20
  • @PascalCuoq Thanks for your comment - I updated my answer to address this point (hopefully adequately) :^) – JVMATL Dec 19 '13 at 15:44
  • I have edited your explanation slightly, I hope you won't mind. – Pascal Cuoq Dec 19 '13 at 15:56
  • The place where **I** am out of my depth is Java. In the question, the behavior “displaying the shortest string of decimal digits that would convert to the same float or double” is actually a behavior of `+` when applied to `string` and `float` (and respectively `string` and `double`), is it not? – Pascal Cuoq Dec 19 '13 at 15:59
  • @PascalCuoq yes - in Java, when you have a_string + , it implicitly converts the expression to `(a_string + String.valueOf(not_a_string))` -- and `String.valueOf()` is overloaded to handle all the primitive types, as well as Object. So if you add (concatenate) a float to a string, it will not promote the float to, say, a double before printing. – JVMATL Dec 19 '13 at 16:13
4

It is because in

double b = a;

the float value is converted (widened) to a double, but in

b == a

the same float value in a is again converted to a double to compare it with b. So, the outcome of the 2 conversions is the same.

Ingo
  • 36,037
  • 5
  • 53
  • 100
3

The reason that printing a and b with:

System.out.println("a : "+a);
System.out.println("b : "+b);

shows different results is because the default floating-point printing for Java shows just enough digits to distinguish the number in its type. In float a=99999.99F;, the value 99999.99 is not exactly representable in float, so it is converted to the nearest representable value (99999.9921875), and a is set to this value.

When a is printed, “99999.99” is all that is needed to show the value.

Then double b=a; sets b to exactly the same value. However, since b is double, there are other representable values in double that are closer to 99999.99. If you had used double b = 99999.99;, the fact that double has more precision means b would have been set to a value closer to 99999.99 (99999.990000000005238689482212066650390625). However, your code has set b to a value farther from 99999.99 than that. So, when b is printed, Java must use more digits to show that its value is not the double that is closest to 99999.99.

Finally, when you compare a and b, the comparison returns true because a and b have exactly the same value.

In summary: a and b are exactly equal, but b is printed with more digits because it is represented more finely in double.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
1

It might be easier to see what's going on if one uses a different value. If, for example, one uses the value 1234512.345f, that value will print as 1234512.4, but if converted to double it will print as 1234512.375. The closest float value to 1234512.345 is precisely equal to 1234512.375; the values on either side are 1234512.25 and 1234512.5; since the value 1234512.4 is closer to 1234512.375 than to either of those, there's no need to use more digits to represent something more precisely. There are many millions of double values between 1234512.375 and 1234512.4; thus, the shorter representation is not adequate for the purposes of showing that quantity as a double. Java will thus use as many digits are are necessary to uniquely identify the value as a double.

supercat
  • 77,689
  • 9
  • 166
  • 211