TransWikia.com

C# calculations differ between const and variable locals?

Stack Overflow Asked by René van den Berg on February 24, 2021

I was setting up a pop quiz for my colleagues about the Banker’s Rounding approach that C# uses in the Math.Round function. But while preparing the question in repl.it I got a result that I thought was pretty weird. At first I was working with an array, but I’ve managed to boil it down to this snippet to find a small reproduction scenario:

class MainClass {
  public static void Main (string[] args) {
    double x = 10.5;
    System.Console.WriteLine(format: "Math.Round({0}) = {1}",
                             arg0: x,
                             arg1: System.Math.Round(x));


    const double y = 10.5;
    System.Console.WriteLine(format: "Math.Round({0}) = {1}",
                             arg0: y,
                             arg1: System.Math.Round(y));
  }
}

This results in the following output:

Math.Round(10.5) = 10
Math.Round(10.5) = 11

(I’ve also tried it with decimal numbers, which didn’t result in any difference between the calculation methods: both resulted in 10 which is correct according to the Banker’s Rounding rule.)

Now, I’m guessing this might have something to do with the const double version getting precompiled, but I’d expect – and I’m not sure this is reasonable – the precompiled version to use the same rules for rounding and/or (I’m not sure what the exact cause is) suffer from the exact same roundoff error – effectively, I’d expect it to perform the exact same calculation, just at a different time.

It is somewhat hard to find more information on this behavior, partially because I’m not sure whether I’ve ran into a bug in Math.Round (which is, apparently, has some issues as well) or something that is related to the precompilation that consts allow, but I’m guessing it’s the latter – and searching for things like "c# const different result" gave me nothing immediately useful.

So my question is, can anyone explain this output in terms of "this only happens if the compiler runs on platform X and the program then runs on platform Y because Z"?

(Edit: sorry, forgot to post the repl.it link. Here it is!)

One Answer

I'm not sure if this is a bug with the mono compiler, but I encountered a similar problem here, and the code change that was made by the contributor here.

My guess is that the Round function is changing the decimal places in the tests to the value X, but with the constant Y the original value does not change.

Below is a possible workaround:

double x = 10.5;
const double y = 10.5;

System.Console.WriteLine(format: "Math.Round({0}) = {1}",
                         arg0: x,
                         arg1: System.Math.Round(x, 0, MidpointRounding.AwayFromZero));
// 11
System.Console.WriteLine(format: "Math.Round({0}) = {1}",
                         arg0: y,
                         arg1: System.Math.Round(y, 0 , MidpointRounding.AwayFromZero)); 
// 11
System.Console.WriteLine(format: "Math.Round({0}) = {1}",
                         arg0: x,
                         arg1: System.Math.Round(y, 0 , MidpointRounding.ToEven));
// 10
System.Console.WriteLine(format: "Math.Round({0}) = {1}",
                         arg0: y,
                         arg1: System.Math.Round(y, 0 , MidpointRounding.ToEven));
// 10

I was able to do Math.Round correctly in .NET, .NET Core 3.0 in both x_86 and x_64 on windows 10.

Perhaps it is something to report on the mono github as a issue. If you do, you can get the system and compiler information with the below commands in the command line window at Repl.it

System info: uname -a

Linux 52d579a3a5fc 5.4.0-1019-gcp #19-Ubuntu SMP Tue Jun 23 15:46:40 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

Complier version: mono --version

Mono JIT compiler version 6.10.0.104 (tarball Fri Jun 26 19:38:24 UTC 2020) Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com TLS: __thread SIGSEGV: altstack Notifications: epoll Architecture: amd64 Disabled: none Misc: softdebug Interpreter: yes LLVM: yes(610) Suspend: hybrid GC: sgen (concurrent by default)

Interesting question! please let me know if it helped you.

Answered by Wesley Pereira da Silveira on February 24, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP