C#计算在const和variable locals之间有所不同?

Ren*_*erg 14 c# double mono constants

我正在为我的同事设置一个关于 C# 在Math.Round函数中使用的银行家舍入方法的快速测验。但是在 repl.it 中准备问题时,我得到了一个我认为很奇怪的结果。起初我使用的是一个数组,但我已经设法将它归结为这个片段以找到一个小的复制场景:

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));
  }
}
Run Code Online (Sandbox Code Playgroud)

这导致以下输出:

Math.Round(10.5) = 10
Math.Round(10.5) = 11
Run Code Online (Sandbox Code Playgroud)

(我也用decimal数字尝试过,这并没有导致计算方法之间有任何差异:两者的结果都是 10,根据银行家的舍入规则是正确的。)

现在,我猜这可能与const double预编译版本有关,但我希望 - 而且我不确定这是否合理 - 预编译版本使用相同的规则进行舍入和/或(我'我不确定确切的原因是什么)遭受完全相同的舍入误差 - 实际上,我希望它执行完全相同的计算,只是在不同的时间。

很难找到有关此行为的更多信息,部分原因是我不确定我是否遇到了错误Math.Round(显然也有一些问题)或与预编译相关const的问题允许,但我猜是后者 - 搜索“c# const different result”之类的东西并没有立即有用。

所以我的问题是,任何人都可以用“这只发生在编译器在平台 X 上运行并且程序然后在平台 Y 上运行,因为 Z”来解释这个输出

(编辑:抱歉,忘了发布 repl.it 链接。在这里!)

小智 1

我不确定这是否是单声道编译器的错误,但我在这里遇到了类似的问题,以及贡献者在这里所做的代码更改。

我的猜测是 Round 函数正在将测试中的小数位更改为值X,但对于常量Y ,原始值不会改变。

以下是一个可能的解决方法:

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
Run Code Online (Sandbox Code Playgroud)

我能够在 Windows 10 上的 x_86 和 x_64 中的 .NET、.NET Core 3.0 中正确执行 Math.Round。

也许应该在 mono github 上报告为一个问题。如果这样做,您可以在 Repl.it 的命令行窗口中使用以下命令获取系统和编译器信息

系统信息:uname -a

Linux 52d579a3a5fc 5.4.0-1019-gcp #19-Ubuntu SMP 6 月 23 日星期二 15:46:40 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

编译器版本:mono --version

Mono JIT 编译器版本 6.10.0.104(tarball UTC 2020 年 6 月 26 日星期五 19:38:24) 版权所有 (C) 2002-2014 Novell, Inc、Xamarin Inc 和贡献者。www.mono-project.com TLS:__thread SIGSEGV:altstack 通知:epoll 架构:amd64 禁用:无 其他:softdebug 解释器:是 LLVM:yes(610) 挂起:混合 GC:sgen(默认情况下并发)

有趣的问题!如果对您有帮助,请告诉我。