Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

difference of float/double conversion betwen VS2012 and VS2015

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!

like image 958
lezebulon Avatar asked Mar 15 '17 09:03

lezebulon


1 Answers

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:

  • https://github.com/dotnet/roslyn/issues/11311
  • https://stackoverflow.com/a/8795656/106159
  • .net float rounding errors at compile-time vs runtime
like image 98
Matthew Watson Avatar answered Sep 30 '22 15:09

Matthew Watson