I have the following simple code :
int speed1 = (int)(6.2f * 10);
float tmp = 6.2f * 10;
int speed2 = (int)tmp;
speed1
and speed2
should have the same value, but in fact, I have :
speed1 = 61
speed2 = 62
I know I should probably use Math.Round instead of casting, but I'd like to understand why the values are different.
I looked at the generated bytecode, but except a store and a load, the opcodes are the same.
I also tried the same code in java, and I correctly obtain 62 and 62.
Can someone explain this ?
Edit : In the real code, it's not directly 6.2f * 10 but a function call * a constant. I have the following bytecode :
for speed1
:
IL_01b3: ldloc.s V_8
IL_01b5: callvirt instance float32 myPackage.MyClass::getSpeed()
IL_01ba: ldc.r4 10.
IL_01bf: mul
IL_01c0: conv.i4
IL_01c1: stloc.s V_9
for speed2
:
IL_01c3: ldloc.s V_8
IL_01c5: callvirt instance float32 myPackage.MyClass::getSpeed()
IL_01ca: ldc.r4 10.
IL_01cf: mul
IL_01d0: stloc.s V_10
IL_01d2: ldloc.s V_10
IL_01d4: conv.i4
IL_01d5: stloc.s V_11
we can see that operands are floats and that the only difference is the stloc/ldloc
.
As for the virtual machine, I tried with Mono/Win7, Mono/MacOS, and .NET/Windows, with the same results.
First of all, I assume that you know that 6.2f * 10
is not exactly 62 due to floating point rounding (it's actually the value 61.99999809265137 when expressed as a double
) and that your question is only about why two seemingly identical computations result in the wrong value.
The answer is that in the case of (int)(6.2f * 10)
, you are taking the double
value 61.99999809265137 and truncating it to an integer, which yields 61.
In the case of float f = 6.2f * 10
, you are taking the double value 61.99999809265137 and rounding to the nearest float
, which is 62. You then truncate that float
to an integer, and the result is 62.
Exercise: Explain the results of the following sequence of operations.
double d = 6.2f * 10;
int tmp2 = (int)d;
// evaluate tmp2
Update: As noted in the comments, the expression 6.2f * 10
is formally a float
since the second parameter has an implicit conversion to float
which is better than the implicit conversion to double
.
The actual issue is that the compiler is permitted (but not required) to use an intermediate which is higher precision than the formal type (section 11.2.2). That's why you see different behavior on different systems: In the expression (int)(6.2f * 10)
, the compiler has the option of keeping the value 6.2f * 10
in a high precision intermediate form before converting to int
. If it does, then the result is 61. If it does not, then the result is 62.
In the second example, the explicit assignment to float
forces the rounding to take place before the conversion to integer.