代码之家  ›  专栏  ›  技术社区  ›  Richard Morgan

C如何评估悬停在中间窗口和编译窗口中的浮点?

  •  7
  • Richard Morgan  · 技术社区  · 15 年前

    我看到在字典中存储双精度数有点奇怪,我很困惑为什么会这样。

    代码如下:

                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会告诉我false,而代码却很高兴地移动到console.writeline。

    这是为C 3.5在Visual Studio 2008中提供的。

    这是浮点精度问题吗?那么为什么第二个if语句有效呢?我觉得我错过了一些非常重要的东西。

    任何见解都会受到赞赏。

    编辑2 (有点目的性的问题):

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

    更新

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

    在同一台计算机上的另一个项目中重新创建此项目时,我也遇到了问题。查看项目设置,我看不到任何差异。看看项目之间的IL,我看不出有什么不同。在反汇编中,除了内存地址外,我没有看到明显的区别。然而,当我调试原始项目时,我看到: screenshot of problem http://i30.tinypic.com/ega874.png

    即时窗口告诉我if是假的,但是代码属于条件。

    无论如何,我认为最好的答案是在这种情况下准备浮点运算。我不能放弃这一点的原因更多的是因为调试器的计算不同于运行时。所以非常感谢布莱恩吉迪恩和斯蒂芬蒂龙的一些非常有见地的评论。

    4 回复  |  直到 15 年前
        1
  •  13
  •   nothrow    15 年前

    这是浮动精度问题。

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

    在ildasm/reflector中查找,它将发出类似

     if (1.0 < 1.0)
                    Console.WriteLine("Wrong");
    
        2
  •  4
  •   Stephen Canon    15 年前

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

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

    • (长双精度)用长双精度计算的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识别为双精度文本并以这种方式处理它。正在用长双精度计算乘法,因为 C有一个脑死亡数字模型 这就是编译器生成代码的方式。

    第二个比较中的乘法要么使用其他两个方法中的一个进行计算(您可以通过尝试“1>1e-3*1e3”来确定哪个方法),要么编译器在编译时计算表达式时,先对乘法结果取整,然后再将其与1.0进行比较。

    如果不通过某些构建设置告诉编译器不要使用扩展精度,则很可能告诉编译器不要使用扩展精度;启用codegen到sse2也可能有效。

        3
  •  2
  •   Community CDub    8 年前

    看答案 here

        4
  •  2
  •   Brian Gideon    15 年前

    嗯…奇怪。我无法再现你的问题。我也在使用C 3.5和Visual Studio 2008。我已经输入了你的例子 确切地 因为它是张贴的,我也没有看到 Console.WriteLine 语句执行。

    另外,第二个if语句正由编译器进行优化。当我检查ildasm/reflector中的调试和发布构建时,我看不到任何证据。这是因为我收到一个编译器警告说在它上面检测到了无法访问的代码。

    最后,我不明白这怎么可能是一个浮点精度问题。为什么C编译器静态地计算两个双精度值与运行时的clr不同?如果真的是这样的话,那么就可以证明C编译器有一个bug。

    编辑: 在多想了一会儿之后,我更确信这是 浮点精度问题。您一定是在编译器或调试器中偶然发现了一个bug,或者您发布的代码并不完全代表正在运行的实际代码。我对编译器中的错误持高度怀疑态度,但调试器中的错误似乎更可能出现。尝试重新构建项目并再次运行它。可能是与exe一起编译的调试信息不同步。