This is what happens on my computer:
(double)(float)0.6
= 0.60000002384185791
(double)0.6f
= 0.60000002384185791
(double)(6/10f)
= 0.6
(double)(float)(6/1
It appears to be rounding the result. Are you displaying the result with the necessary digits of precision? You can use this c# class from Jon Skeet to get the exact numeric representation of the result printed out.
Note that ToString()
will not always print all of the digits, nor will the Visual Studio debugger.
First, its important to bear in mind that 0.6
cannot be accurately represented as a float
however it can be accurately represented as a double
(the inaccuracies of floating point arithmetic are well documented, if its not clear why 0.6
cannot be accurately represented as a float, try this link)
The reason why you are seeing the above behaviour is down to the compiler - if you take a look at the compiled assembly in reflector then whats going on here is a little clearer:
(UPDATE I've changed the code so that it doesn't use Console.WriteLine
, as I realised that the compiler was choosing an overload for you, which confused the situation)
// As written in source
var j = (double)(float)0.6;
var k = (double)0.6f;
var l = (double)(6/10f);
var m = (double)(float)(6/10f);
// Code as seen by Reflector
double j = 0.60000002384185791;
double k = 0.60000002384185791;
double l = 0.6;
double m = 0.6;
Why the compiler chooses to compile in this particular way is beyond me (fyi, this is all with optimisations turned off)
Some interesting other cases:
// Code
var a = 0.6;
var b = (double)0.6;
var c = 0.6f;
var d = (float)0.6;
var e = 6 / 10;
var f = 6 / (10f);
var g = (float)(6 / 10);
var h = 6 / 10f;
var i = (double)6 / 10;
// Prints out 0.60000002384185791
double n = (float)0.6;
double o = f;
// As seen by Reflector
double a = 0.6;
double b = 0.6;
float c = 0.6f;
float d = 0.6f;
int e = 0;
float f = 0.6f;
float g = 0f;
float h = 0.6f;
double i = 0.6;
double n = 0.60000002384185791;
double o = f;
The compiler only seems to do the above trick in a couple of special cases, why it does this only when casting to a double is completely beyond me!
The rest of the time it seems to do some trickery to make floating point arithmetic seem to work where in fact it normally wouldn't.
If I were a betting man, I'd say the difference is in where the coercion is happening. In the latter two examples (the ones with 6/10f), there are two literals that are both whole numbers (the integer 6 and the float 10.00000000...). The division appears to be happening after the coercion, at least in the compiler you're using. In the first two examples, you have a fractional float literal (0.6) which cannot be adequately expressed as a binary value within the mantissa of a float. Coercing that value to a double cannot repair the damage that was already done.
In the environments that are producing completely consistent results, the division is occurring before the coercion to double (the 6 will be coerced to a float for the division to match the 10, the division is carried out in float space, then the result is coerced to a double).