1

Summation of these numbers give different results in .NET Core / C# and on other compilers.

3987908.698692091 + 92933945.11382028 + 208218.11919727124 + 61185833.06829034

.NET Core / C# : 158315904.99999997

Others: 158315905

Clearly the .NET Core / C# one is deviated.

Here is the code in C#:

double[] no = {
    3987908.698692091,
    92933945.11382028,
    208218.11919727124,
    61185833.06829034
};

Console.WriteLine("{0}", no.Sum());

Here is the code in C++

vector<double> no = {
    3987908.698692091,
    92933945.11382028,
    208218.11919727124,
    61185833.06829034
    };
    
cout << setprecision(16);
cout << "sum: " << sum(no) << " \n";

double sum(vector<double> &fa)
{
    double sum = 0.0;
    for(double f : fa)
        sum = sum + f;
    return sum;
}

PS: Using decimal also gives the same outcome. I believe mono C# compiler might give the same result as the C++ one.

Is there a way to fix the deviation problem either with compiler options or somehow inside C#?

user818117
  • 420
  • 1
  • 5
  • 15
  • 3
    Since the number with 11 decimal places ends with `24` and the others are either 8 or 9 decimal places, clearly any answer that claims it should be a whole number is wrong to some extent. As always, be aware of the range of precisions that may be used in representing floating point numbers (and especially e.g. 80-bit vs 64-bit at times for Intel Processors) and don't focus attention on spurious accuracy. – Damien_The_Unbeliever Apr 28 '23 at 07:22
  • E.g. there are very few measurements you could actually take that would be accurate to 17 significant figures, which is what your `208218.11919727124` would claim to be. – Damien_The_Unbeliever Apr 28 '23 at 07:24
  • I have tried on x64, and arm64, same outcome, in this particular case spurious accuracy is very important,thanks – user818117 Apr 28 '23 at 07:24
  • 2
    Why do you think 158315905 is the correct value? – Magnus Apr 28 '23 at 07:41
  • 2
    Which version of .net core are you using? I've run the following for decimals on .net 7 and I obtained an exact number: `decimal d1 = 3987908.698692091M;` `decimal d2 = 92933945.11382028M;` `decimal d3 = 208218.11919727124M;` `decimal d4 = 61185833.06829034M;` `Console.WriteLine($"Sum is: {d1+d2+d3+d4}");` `Sum is: 158315904.99999998224` – Albert Apr 28 '23 at 07:48
  • 1
    @Albert `decimal` != `double` – Matthew Watson Apr 28 '23 at 09:21
  • 2
    @MatthewWatson - there were some comments, since deleted, asking the OP to try decimal. The question was then edited to say (and it still does currently) that they get the same behaviour with `decimal`. – Damien_The_Unbeliever Apr 28 '23 at 11:40
  • Thanks for providing the context @Damien_The_Unbeliever – Matthew Watson Apr 28 '23 at 13:29

1 Answers1

1

This is only affecting the formatted output - the underlying numbers have not changed.

This was changed with .NET Core 3.0

If you change the output to:

Console.WriteLine("{0:G15}", no.Sum());

the output will be the same for both .NET 4.8 and .NET Core 3.0 and later.

The change to the formatting that you're seeing was made to implement IEEE 754-2008 correctly.

In particular, this requirement mandated the change:

The standard requires operations to convert between basic formats and external character sequence formats. Conversions to and from a decimal character format are required for all formats. Conversion to an external character sequence must be such that conversion back using round to nearest, ties to even will recover the original number.

Obviously the default round-trip conversion wouldn't work for .NET Framework because a value of 158315904.99999997 was converted to a string "158315905" which, when converted back to a binary floating point number, would differ from the original.

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276