Type conversion strange bug
-
Yesterday I've caught a very strange bug float a = 81.1666641F; float b = 18F; float c = 6.3166666F; float d = (a - b) / c; int e = (int)((a - b) / c); int f = (int)d; Console.WriteLine(d); Console.WriteLine(e); Console.WriteLine(f); -------------------------- OUTPUT: 10.0 9 10
-
Yesterday I've caught a very strange bug float a = 81.1666641F; float b = 18F; float c = 6.3166666F; float d = (a - b) / c; int e = (int)((a - b) / c); int f = (int)d; Console.WriteLine(d); Console.WriteLine(e); Console.WriteLine(f); -------------------------- OUTPUT: 10.0 9 10
It seems that for any reason the c#-compiler handles a, b and c in the line int e = (int)((a - b) / c); as they were doubles: double d = ((double)a - (double)b) / (double)c); -> 9.99999969804507 If you change the calculation of e: int e = (int)(float)((a - b) / c); then you get OUTPUT: 10 10 10 Very strange.
-
It seems that for any reason the c#-compiler handles a, b and c in the line int e = (int)((a - b) / c); as they were doubles: double d = ((double)a - (double)b) / (double)c); -> 9.99999969804507 If you change the calculation of e: int e = (int)(float)((a - b) / c); then you get OUTPUT: 10 10 10 Very strange.
mabo42 wrote:
Very strange.
But well documented, the more annoying one which seems to have no obvious explanation is trying to take the modulus of a floating-point number. This caused big problems for me, ended up rewriting the calculations to use integers with a final scaling factor at the end.
-
mabo42 wrote:
Very strange.
But well documented, the more annoying one which seems to have no obvious explanation is trying to take the modulus of a floating-point number. This caused big problems for me, ended up rewriting the calculations to use integers with a final scaling factor at the end.
-
Once more about "well documented": I've found this in the c# docs [^]: The following table shows the predefined implicit numeric conversions. Implicit conversions might occur in many situations, including method invoking and assignment statements. From To ... float double ...
float a = 81.1666641F;
float b = 18F;
float c = 6.3166666F;float f = (a - b) / c; // here no implicit conversion occurs
int i1 = (int)(f); // -> 10
int i2 = (int)((a - b) / c); // -> 9, imlicit conversion to double, caused by the explicit conversion to intThe doc is still correct (might = could be, but not have to), but I would not expect this behavior. Lessons learned!
-
Once more about "well documented": I've found this in the c# docs [^]: The following table shows the predefined implicit numeric conversions. Implicit conversions might occur in many situations, including method invoking and assignment statements. From To ... float double ...
float a = 81.1666641F;
float b = 18F;
float c = 6.3166666F;float f = (a - b) / c; // here no implicit conversion occurs
int i1 = (int)(f); // -> 10
int i2 = (int)((a - b) / c); // -> 9, imlicit conversion to double, caused by the explicit conversion to intThe doc is still correct (might = could be, but not have to), but I would not expect this behavior. Lessons learned!
-
Once more about "well documented": I've found this in the c# docs [^]: The following table shows the predefined implicit numeric conversions. Implicit conversions might occur in many situations, including method invoking and assignment statements. From To ... float double ...
float a = 81.1666641F;
float b = 18F;
float c = 6.3166666F;float f = (a - b) / c; // here no implicit conversion occurs
int i1 = (int)(f); // -> 10
int i2 = (int)((a - b) / c); // -> 9, imlicit conversion to double, caused by the explicit conversion to intThe doc is still correct (might = could be, but not have to), but I would not expect this behavior. Lessons learned!
That's not the reason. Actually, there's no conversion to double here. The C# specification just says somewhere something like "floating point calculations may be run with greater precision than specified". The reason is that variables are typed float or double, but floating point values on the MSIL evaluation stack are typed "F", a platform-specific floating point type. float: 32-bit double: 64-bit "F": 80-bit (because that's what the floating point unit of x86 processors internally uses)
-
That's not the reason. Actually, there's no conversion to double here. The C# specification just says somewhere something like "floating point calculations may be run with greater precision than specified". The reason is that variables are typed float or double, but floating point values on the MSIL evaluation stack are typed "F", a platform-specific floating point type. float: 32-bit double: 64-bit "F": 80-bit (because that's what the floating point unit of x86 processors internally uses)
-
Hi Daniel, may be, I'm a bit on the slow side, but why the difference:
int i1 = (int) ((a - b) / c); // -> 9
int i2 = (int)(float)((a - b) / c); // -> 10int i1 = (int) (9.9999999999999); // -> 9
int i2 = (int)(float)(9.9999999999999); // -> 10double has 15 significant decimal digits, float only about 7. When casting a value of type F (remember, 80-bit) to int, the digits after the decimal point are truncated. But when casting a value of type F to float, it is rounded to the next value. Simple rule: don't trust floating-point numbers. Never compare them for equality without rounding because possible errors in the calculation can created undefined results - e.g. in the set of floating point numbers, a/b is not always equal to (2*a)/(2*b). Never cast them to int without rounding if you want defined results for numbers equal to a whole integer (remember, there's no such thing as a floating point value equal to a whole integer if it came out of a calculation - it often works, but doesn't always have to)
-
int i1 = (int) (9.9999999999999); // -> 9
int i2 = (int)(float)(9.9999999999999); // -> 10double has 15 significant decimal digits, float only about 7. When casting a value of type F (remember, 80-bit) to int, the digits after the decimal point are truncated. But when casting a value of type F to float, it is rounded to the next value. Simple rule: don't trust floating-point numbers. Never compare them for equality without rounding because possible errors in the calculation can created undefined results - e.g. in the set of floating point numbers, a/b is not always equal to (2*a)/(2*b). Never cast them to int without rounding if you want defined results for numbers equal to a whole integer (remember, there's no such thing as a floating point value equal to a whole integer if it came out of a calculation - it often works, but doesn't always have to)
-
Yesterday I've caught a very strange bug float a = 81.1666641F; float b = 18F; float c = 6.3166666F; float d = (a - b) / c; int e = (int)((a - b) / c); int f = (int)d; Console.WriteLine(d); Console.WriteLine(e); Console.WriteLine(f); -------------------------- OUTPUT: 10.0 9 10