C#如何在悬停和即时窗口与编译窗口中计算浮点?

C#如何在悬停和即时窗口与编译窗口中计算浮点?,c#,math,floating-point,C#,Math,Floating Point,我发现在字典里储存双倍数字有些奇怪,我不明白为什么 代码如下: Dictionary<string, double> a = new Dictionary<string, double>(); a.Add("a", 1e-3); if (1.0 < a["a"] * 1e3) Console.WriteLine("Wrong"); if

我发现在字典里储存双倍数字有些奇怪,我不明白为什么

代码如下:

            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");
字典a=新字典();
a、 添加(“a”,1e-3);
如果(1.0
第二个if语句按预期工作;1.0不小于1.0。现在,第一个if语句的计算结果为true。非常奇怪的是,当我将鼠标悬停在if上时,intellisense告诉我false,但代码却很高兴地移动到Console.WriteLine

这是针对VisualStudio2008中的C#3.5

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

任何见解都值得赞赏

Edit2(稍微改变一下问题的目的):

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

更新

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

我在同一台机器上的另一个项目中重新创建它时也遇到问题。看看项目设置,我看不出有什么不同。看看项目之间的IL,我看不出有什么不同。查看反汇编,我没有看到明显的差异(除了内存地址)。然而,当我调试原始项目时,我看到:

immediate窗口告诉我if为false,但代码属于条件类型


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

这是浮点精度问题

第二条语句之所以有效,是因为编译器在发出.exe之前对表达式1e-3*1e3进行计数

在ILDasm/反射器中查找,它会发出类似

 if (1.0 < 1.0)
                Console.WriteLine("Wrong");
if(1.0<1.0)
控制台。写入线(“错误”);

查看答案

嗯……奇怪。我无法重现你的问题。我也在使用C#3.5和VisualStudio2008。我输入的示例与发布的示例完全相同,我没有看到执行
Console.WriteLine
语句

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

最后,我不明白这怎么会是一个浮点精度问题。为什么C#编译器静态计算两个double的方式与CLR在运行时的方式不同?如果真的是这样的话,那么我们可以说C#编译器有一个bug


编辑:在仔细考虑之后,我更加确信这不是一个浮点精度问题。您一定是在编译器或调试器中偶然发现了错误,或者您发布的代码并不完全代表您正在运行的实际代码。我对编译器中的错误表示高度怀疑,但调试器中的错误似乎更有可能出现。尝试重新生成项目并再次运行它。可能是与exe一起编译的调试信息不同步或其他什么。

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

根据1e-3是按双精度还是长双精度计算,以及乘法是按双精度还是长双精度计算,可以得到以下三个结果中的任意一个:

  • (长双精度)在长双精度中计算的1e-3*1e3为1.0-ε
  • (双精度)双精度计算的1e-3*1e3正好是1.0
  • (双精度)长双精度计算的1e-3*1e3为1.0+ε
显然,第一个比较,即未能满足您期望的比较,正在按照我列出的第三个场景中描述的方式进行评估。1e-3被舍入为双精度,或者是因为您正在存储它并再次加载它,这会强制舍入,或者是因为C#将1e-3识别为双精度文字并以这种方式处理。乘法是用长双精度计算的,因为C#有一个大脑死亡的数字模型,这是编译器生成代码的方式

第二次比较中的乘法要么使用其他两种方法中的一种进行求值(您可以通过尝试“1>1e-3*1e3”来确定是哪种方法),要么编译器在编译时计算表达式时,在将乘法结果与1.0进行比较之前对乘法结果进行四舍五入


在没有通过某些构建设置告诉编译器不要使用扩展精度的情况下,您可能会告诉编译器不要使用扩展精度;将codegen启用到SSE2也可能起作用。

1e3是浮点常量还是双常量?如果你能接受数学精度问题,为什么还要问?问题是1e-3无法准确表示,因此1e3*1e-3不是1.0,它很接近,但不完全,这意味着它失败。“1e3”被编译为双精度,因为我在IL输出中看到ldc.r8指令。@Lasse:我接受在运行时发生这种情况的原因;我想知道为什么我的调试器(鼠标悬停在