Why is (double)0.6f > (double)(6/10f)?
Asked Answered
W

3

10

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/10f)
= 0.6

6/10f is also a float,how come it can be precisely 0.6?
In my mind (double)(6/10f) should also be 0.60000002384185791. Can someone help explain this? thanks!

Wyant answered 20/9, 2010 at 4:49 Comment(8)
By the way in Java, the result is 0.6000000238418579 for all of the above.Cecil
I'm using immediate window. I've tested in program, they are not equal. So it not a matter of foramtting.Wyant
#1778868Sentimentality
@Thomas, IEEE754 double precision lets you specify much closer to 0.6 with the 0x3fe3333333333333 value.Nay
Well, at least it's consistent on mono (0.600000023841858 for all 4)Melantha
I'm guessing the binary representation of these two are different: "(float)(6/10.0)" and "(float)(6/10.0f)"Wyant
In general, don't ever try to expect floats/doubles to be equal.Abyssal
Possible duplicate of Why are floating point numbers inaccurate?Rappel
S
5

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.

Spectrochemistry answered 20/9, 2010 at 5:8 Comment(5)
Interestingly enough, if you use float, all of them will print 0.6 (as seen on ideone, also tested with Visual C#)Melantha
Thanks everybody, I think its indeed a compiler specific issue.Wyant
Also I found that: float float3 = 0.6f; int int1 = 6; float1 = 10; Assert.IsTrue(int1 / float1 >= float3);//fails, In my code float3 is an argument!Wyant
0.6 CANNOT be accurately represented as a double.Lightfingered
@Rick is right, and this answer is wrong. 3/5 repeats infinitely in base 2, so it can not be represented exactly as a double or as any finite binary number.Compensation
N
2

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.

Nitin answered 20/9, 2010 at 4:57 Comment(0)
V
1

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).

Vargas answered 20/9, 2010 at 5:23 Comment(1)
You can always take a boo at what's happened by using ildasm and looking at the intermediate assembly created.Vargas

© 2022 - 2024 — McMap. All rights reserved.