Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# rounding differently depending on platform?

Tags:

c#

rounding

I have this tiny piece of code

double s = -2.6114289999999998;
double s7 = Math.Round(s, 7);
double s5 = Math.Round(s, 5);
double s6 = Math.Round(s, 6);

With Platform = Any CPU, I get

s7: -2.611429   
s5: -2.61143    
s6: -2.611429   

With Platform = x64, I get

s7: -2.6114289999999998 
s5: -2.61143    
s6: -2.6114289999999998

Why? (Output copied from VS's Locals window)


The whole piece of code is:

    private void btnAlign_Click(object sender, EventArgs e)
    {
        double s = -2.6114289999999998;
        double s7 = Math.Round(s, 7);
        double s5 = Math.Round(s, 5);
        double s6 = Math.Round(s, 6);
    }
like image 224
beyond Avatar asked Nov 15 '17 08:11

beyond


2 Answers

The value -2.611429 cannot be represented using 64-bit floating point. When compiling in 32-bit mode the value will instead use 80-bit (extended precision).

like image 196
l33t Avatar answered Oct 04 '22 04:10

l33t


On x64, the SSE2 FPU is used and on x86 the x87 FPU is used.

It is possible (although not advised) to change the x87 precision to be the same as the SSE2 precision (i.e., use lower precision).

You can do that via the _controlfp() API.

The following program demonstrates. Run this program in x86 mode and you'll see how the use of _controlfp(0x00030000, 0x00020000) causes the output to change to be similar to (but not quite the same!) as the x64 version:

using System;
using System.Runtime.InteropServices;

namespace ConsoleApp3
{
    class Program
    {
        static void Main()
        {
            double s = -2.6114289999999998;

            Console.WriteLine(Math.Round(s, 7).ToString("R")); // -2.611429

            if (!Environment.Is64BitProcess)
                _controlfp(0x00030000, 0x00020000);

            Console.WriteLine(Math.Round(s, 7).ToString("R")); // -2.61142897605896
        }

        [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern uint _controlfp(uint newcw, uint mask);
    }
}

However, you should not mess around with the FPU in this way (and if you do, you should revert to the previous setting asap).

like image 38
Matthew Watson Avatar answered Oct 04 '22 02:10

Matthew Watson