I have the following code:
float a = 0.02f * 28f;
double b = (double)a;
double c = (double)(0.02f * 28f);
Console.WriteLine(String.Format(" {0:F20}", b));
Console.WriteLine(String.Format(" {0:F20}", c));
However it returns different results whether it's compiled from VS2012 or VS2015 (both have "standard" settings)
In VS2012
0,56000000238418600000
0,55999998748302500000
In VS2015:
0,56000000238418600000
0,56000000238418600000
VS2012 dissasembly:
float a = 0.02f * 28f;
0000003a mov dword ptr [ebp-40h],3F0F5C29h
double b = (double)a;
00000041 fld dword ptr [ebp-40h]
00000044 fstp qword ptr [ebp-48h]
double c = (double)(0.02f * 28f);
00000047 fld qword ptr ds:[001D34D0h]
0000004d fstp qword ptr [ebp-50h]
VS2015 dissasembly:
float a = 0.02f * 28f;
001E2DE2 mov dword ptr [ebp-40h],3F0F5C29h
double b = (double)a;
001E2DE9 fld dword ptr [ebp-40h]
001E2DEC fstp qword ptr [ebp-48h]
double c = (double)(0.02f * 28f);
001E2DEF fld dword ptr ds:[1E2E7Ch]
001E2DF5 fstp qword ptr [ebp-50h]
As we can see the disassembly is not identical in both cases, is this normal? Could this be a bug in VS2012 or VS2015 ? Or is this behaviour controlled by some specific setting that was changed? thanks!
The issue here is that in the Roslyn compiler, constant floating point calculations done at compile time are performed slightly differently from earlier versions - which is resulting in different behaviour.
However, this is not a bug because of this section of the C# standard:
4.1.6 Floating-point types
Floating-point operations may be performed with higher precision than the result type of the operation. For example, some hardware architectures support an “extended” or “long double” floating-point type with greater range and precision than the double type, and implicitly perform all floating-point operations using this higher precision type. Only at excessive cost in performance can such hardware architectures be made to perform floating-point operations with less precision, and rather than require an implementation to forfeit both performance and precision, C# allows a higher precision type to be used for all floating-point operations. Other than delivering more precise results, this rarely has any measurable effects. However, in expressions of the form x * y / z, where the multiplication produces a result that is outside the double range, but the subsequent division brings the temporary result back into the double range, the fact that the expression is evaluated in a higher range format may cause a finite result to be produced instead of an infinity. To force a value of a floating point type to the exact precision of its type, an explicit cast can be used.
What's happening is that you are seeing the results of undefined behaviour resulting from the above, where the floating point calculation being done at compile time by the Roslyn compiler is using a different precision from the calculation done at compile time by earlier compilers.
Note that there was actually a bug in the initial release of the Roslyn compiler which was fixed by Update 2: https://github.com/dotnet/roslyn/issues/7262 - but this is just a point of interest and is not directly related to the differences in results between VS2012 and VS2015 (with Update 2).
Also see the following for more details:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With