【问题标题】:C# calculations differ between const and variable locals?C# 计算在 const 和 variable locals 之间有什么不同?
【发布时间】:2020-09-30 14:26:50
【问题描述】:

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

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));
  }
}

这会产生以下输出:

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

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

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

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

所以我的问题是,谁能解释这个输出:“这只发生在编译器在平台 X 上运行,然后程序因为 Z 在平台 Y 上运行时才会发生这种情况”?

(编辑:抱歉,忘记发布 repl.it 链接。Here 是!)

【问题讨论】:

  • @JohnathanBarclay 他说他在 repl.it 中运行它,在那里可以重现。
  • repl.it 在撰写本文时在单声道 6.10.0.104 下运行
  • Can't reproduce 在小提琴中。
  • @MatthewWatson 看起来像。在这里测试:jdoodle.com/compile-c-sharp-online 使用 Mono 5.10.1,两者都是 10。Mono 6.0 产生 10 和 11。
  • 真是奇怪的错误。 10.5 在 IEEE 754 中完全可以表示,没有四舍五入的问题,所以我们不能把它归咎于此。

标签: c# mono constants double


【解决方案1】:

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

我的猜测是 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

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

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

系统信息:uname -a

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

编译器版本:mono --version

Mono JIT 编译器版本 6.10.0.104(tarball Fri Jun 26 19:38:24 UTC 2020) 版权所有 (C) 2002-2014 Novell, Inc、Xamarin Inc 和贡献者。 www.mono-project.com TLS:__thread SIGSEGV:altstack 通知:epoll 架构:amd64 禁用:无 杂项:软调试 翻译:是的 LLVM:是(610) 暂停:混合 GC:sgen(默认并发)

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

【讨论】:

    猜你喜欢
    • 2019-11-16
    • 2015-02-20
    • 2013-07-22
    • 1970-01-01
    • 1970-01-01
    • 2023-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多