I know that floating point variables loose precision while casting. But what I don't understand is, why a cast from a smaller primitive to a bigger one is unprecise but vice versa not. I would understand if it happens from double to float but it's the other way around. Why is this so?
See the results from these two tests:
@Test
public void castTwoPrimitiveDecimalsUnpreciseToPrecise()
{
float var1 = 6.2f;
double var2 = var1;
assertThat(var2, is(6.2d)); //false, because it's 6.199999809265137
}
@Test
public void castTwoPrimitiviesDecimalsPreciseToUnpresice()
{
double var1 = 7.6d;
float var2 = (float)var1;
assertThat(var2, is(7.6f)); //true
}
-
6The problem is that none of the numbers you're using are actually 6.2 or 7.6...Jon Skeet– Jon Skeet2013年11月16日 16:01:31 +00:00Commented Nov 16, 2013 at 16:01
-
1But some are more different from 6.2 and 7.6 than others.Patricia Shanahan– Patricia Shanahan2013年11月16日 16:08:46 +00:00Commented Nov 16, 2013 at 16:08
2 Answers 2
The precision issue is in the initialization of your variables, not in the conversion.
In the first case, you start with a number that is only the float approximation to decimal 6.2. The conversion to double gets a double with exactly the same value as that float approximation. You then compare it against the much closer double approximation, so of course it does not match.
In the second case, you start with the double approximation to decimal 7.6. You then convert it to float. That will round the double to a float. Rounding twice, on the original conversion to double and on the cast to float, could conceivably produce a different answer from directly converting a number to float, but usually you will get the float approximation. You then compare it to the float approximation, so it is not surprising you get a match.
Comments
Suppose someone measures a peg with a ruler and determines that it/s 3/8" diameter. The other person measures a hole with a calipers and discovers that it's 9.5267mm. Will the peg fit in the hole?
If one converts the cruder measurement to the higher-precision form, one finds that the hole appears to be 0.0017mm (i.e. about 1/15000") larger than the peg. If one converts them both to the lower-precision form, one will find that the values are indistinguishable.
If the peg that was measured as 3/8" is in fact precisely 9.525mm [the exact metric equivalent], such a measurement may be converted to a higher-resolution form without conversion loss. If, however, it simply means "something that's closer to the 3/8" mark than to 23/64" or 25/64", such conversion will cause a value which would generally be expected to be an approximation to be interpreted as being more precise than it really is.
Some people would regard the fact that the peg and hole are regarded as different sizes as being a good thing. Personally, I would think it would be better to regard them as being indistinguishable. With the measurements as described, there's no particular reason to believe with any certainty that the peg is bigger than the hole; it probably isn't exactly equal, but calling them indistinguishable is more accurate than saying the peg is bigger.
Personally, I detest language rules that require values to be written as or cast to [single-precision] float merely to shut up the compiler. If one wants to set a float equal to the value closest to 4.7, one should be able to simply say:
float f=4.7;
and achieve that effect. A person who sees:
static final float coef = 4.7f;
... some time later
float f = coef;
double d = coef;
will have no way of knowing whether the goal was to set d to the double value equal to the float value closest to 4.7, or whether the goal was to set d to the double value closest to 4.7. Unfortunately, Java provides no means by which one can declare a constant which can be silently assigned to float without an explicit cast, but which when assigned to double will use the precision available in that type.
Incidentally, if my goal was to set some double variable v equal the value of the float closest to the specified numeric value of coef, rather than the value of the double closest to that numeric value, I'd probably code it very explicitly:
v = (double)(float)coef;
That would make clear and unambiguous the fact that the programmer was expecting and intending that a double would be assigned a value which had been previously rounded to float precision. Absent the (double) typecast, one would have to consider the possibilities that the programmer might have expected v to be a float (e.g. because when the code was written, it was a float, but since then it was changed to double). Absent the (float) typecast, one would have to consider the possibility that the programmer was expecting coef to be a double (e.g. because it had been, but someone changed it to a float to allow it to be assigned to variables of float type without the compiler squawking).