C#如何评估悬停在中间窗口和中间窗口上的浮点数与编译后的浮点数?

问题描述 投票:7回答:4

我在字典中存储双打时看到了一些奇怪的东西,我对此感到困惑。

这是代码:

            Dictionary<string, double> a = new Dictionary<string, double>();
            a.Add("a", 1e-3);

            if (1.0 < a["a"] * 1e3)
                Console.WriteLine("Wrong");

            if (1.0 < 1e-3 * 1e3)
                Console.WriteLine("Wrong");

第二个if语句按预期工作; 1.0不小于1.0。现在,第一个if语句的计算结果为true。非常奇怪的是,当我将鼠标悬停在if上时,intellisense告诉我错误,但代码很高兴地移动到Console.WriteLine。

这适用于Visual Studio 2008中的C#3.5。

这是浮点精度问题吗?那为什么第二个if语句有效呢?我觉得我在这里缺少一些非常基本的东西。

任何见解都表示赞赏。

Edit2(重新提出问题):

我可以接受数学精度问题,但我现在的问题是:为什么悬停过度评估正确?中间窗口也是如此。我将第一个if语句中的代码粘贴到中间窗口中,并评估为false。

更新

首先,非常感谢所有伟大的答案。

我也在同一台机器上的另一个项目中重新创建这个问题。看看项目设置,我看不出任何差异。看看项目之间的IL,我认为没有区别。看看反汇编,我发现没有明显的差异(除了内存地址)。然而,当我调试原始项目时,我看到:screenshot of problem

即时窗口告诉我if是否为false,但代码属于条件。

无论如何,我认为最好的答案是在这些情况下为浮点运算做准备。我不能放弃这个的原因更多的是调试器的计算与运行时的不同。非常感谢Brian Gideon和stephentyrone的一些非常有见地的评论。

c# math floating-point
4个回答
13
投票

这是浮动精度问题。

第二个语句有效,因为编译器在发出.exe之前计算表达式1e-3 * 1e3。

在ILDasm / Reflector中查找它会发出类似的东西

 if (1.0 < 1.0)
                Console.WriteLine("Wrong");

4
投票

这里的问题非常微妙。 C#编译器不会(总是)发出以double形式进行计算的代码,即使这是您指定的类型。特别是,它发出的代码使用x87指令以“扩展”精度进行计算,而不会将中间结果舍入为两倍。

根据1e-3是被评估为double还是long double,以及乘法是以double还是long double计算,可以得到以下三个结果中的任何一个:

  • (long double)以long double计算的1e-3 * 1e3为1.0 - epsilon
  • (double)以double计算的1e-3 * 1e3正好是1.0
  • (double)以long double计算的1e-3 * 1e3是1.0 + epsilon

显然,第一次比较,即未达到预期的比较,正按照我列出的第三种情况中描述的方式进行评估。 1e-3被舍入为双倍,因为你正在存储它并再次加载它,这会强制舍入,或者因为C#将1e-3识别为双精度文字并以这种方式对待它。乘法正在以long double进行评估,因为 C#有一个脑死亡的数字模型 这就是编译器生成代码的方式。

第二次比较中的乘法要么是使用其他两种方法中的一种进行评估,(你可以通过尝试“1> 1e-3 * 1e3”来计算出哪一个),或者编译器在比较之前对乘法的结果进行舍入。在编译时评估表达式时为1.0。

如果没有通过某些构建设置告诉编译器,可能会告诉编译器不要使用扩展精度;启用codegen到SSE2也可以工作。


2
投票

看到答案here


2
投票

嗯...奇怪。我无法重现您的问题。我也在使用C#3.5和Visual Studio 2008。我完全按照发布的方式输入了你的例子,我没有看到Console.WriteLine语句执行。

此外,第二个if语句正在由编译器进行优化。当我在ILDASM / Reflector中检查调试和发布版本时,我没有看到它的证据。这是因为我得到一个编译器警告,说它上面检测到无法访问的代码。

最后,我不知道这可能是一个浮点精度问题。为什么C#编译器会在运行时以静态方式评估两个双精度值?如果真的如此,那么可以说C#编译器有一个bug。

编辑:在更多地考虑之后,我更加确信这不是浮点精度问题。您必须偶然发现编译器或调试器中的错误,或者您发布的代码并不完全代表您正在运行的实际代码。我对编译器中的错误持高度怀疑态度,但调试器中的错误似乎更有可能。尝试重建项目并再次运行它。也许与exe一起编译的调试信息也不同步。

© www.soinside.com 2019 - 2024. All rights reserved.