【问题标题】:Floating point inconsistency between expression and assigned object表达式和分配对象之间的浮点不一致
【发布时间】:2012-11-08 14:18:24
【问题描述】:

这让我很惊讶 - 相同的算法会根据其执行方式给出不同的结果:

> 0.1f+0.2f==0.3f
False

> var z = 0.3f;
> 0.1f+0.2f==z
True

> 0.1f+0.2f==(dynamic)0.3f
True

(在 Linqpad 中测试)

发生了什么事?


编辑:我明白为什么浮点运算不精确,但不明白为什么它会不一致

古老的 C 可靠地确认 0.1 + 0.2 == 0.3 适用于单精度浮点数,但不适用于双精度浮点数。

【问题讨论】:

  • -1:这个副本太多了。只需查看侧边栏和标题相关下的链接
  • @DanielHilgarth 在我看来这不是关于浮点的常见问题,而是关于编译器执行的计算与运行时执行的计算之间的差异
  • @DanielHilgarth:您不能简单地将涉及浮点的所有内容都视为浮点不精确。一些浮点问题是解释浮点如何运作的机会。请注意,这个问题表明0.1f+0.2fx 都具有Single 类型。所以有一个问题,为什么0.1f+0.2f 的赋值似乎没有保留值,即使类型没有改变。这是 C# 语义的问题。你能显示一个精确的副本吗?如果您可以识别出完全重复,那么建议将此问题作为完全重复来解决。
  • 这个问题由this Eric Lippert answer 回答,是一个类似但不完全重复的问题。

标签: c# .net floating-point


【解决方案1】:

我强烈怀疑您可能会发现在使用和不使用调试器以及在发布配置和调试配置中运行此代码时会得到不同的结果。

在第一个版本中,您正在比较两个表达式。 C# 语言允许以比源类型更高精度的算术计算这些表达式。

在第二个版本中,您将加法结果分配给局部变量。 在某些情况下,这将强制将结果截断为 32 位 - 导致不同的结果。在其他情况下,CLR 或 C# 编译器会意识到它可以优化掉局部变量。

来自 C# 4 规范的第 4.1.6 节:

浮点运算可以以比运算结果类型更高的精度执行。例如,某些硬件体系结构支持比double 类型具有更大范围和精度的“扩展”或“长双精度”浮点类型,并隐式执行具有更高精度类型的所有浮点运算。只有在性能成本过高的情况下,才能使此类硬件架构以更少的精度执行浮点运算。 C# 允许将更高精度的类型用于所有浮点运算,而不是要求实现同时丧失性能和精度。除了提供更精确的结果之外,这很少有任何可衡量的影响。

编辑:我没有尝试编译这个,但是在 cmets 中,Chris 说第一个表单根本没有在执行时被评估。以上仍然适用(我稍微调整了我的措辞) - 它只是将常量的评估时间从执行时间转移到编译时间。只要它的行为方式与 a 有效评估相同,我觉得这没问题 - 所以编译器自己的常量表达式评估也可以使用更高精度的算术。

【讨论】:

  • 有趣的答案,但我不确定它是否正确。当我查看 LinqPad 生成的 IL 时,它似乎已经完全优化了它,使得 Console.WriteLine((0.1f+0.2f)==0.3f); 变为 ldc.i4.0,然后调用 Console.WriteLine。这向我表明,实际上是编译器优化导致了这种情况,与发送到硬件的指令无关。
  • @Chris 该特定表达式完全基于编译时常量,因此它可以(并且显然正在)在编译时而不是运行时进行评估。
  • @Servy:确实。这很有意义。没有意义的是为什么它评估为假......
  • @Chris:嗯,它可以是相同的推理,只是在早期应用 - 在编译而不是在执行时。将编辑我的答案。
  • @JonSkeet:是的,我后来得出了这个结论,但答案中的澄清不会有什么坏处。 :) 我想我最初只是假设编译器是万无一失的。 :)
猜你喜欢
  • 1970-01-01
  • 2021-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-13
  • 2015-05-06
  • 2011-10-27
  • 2022-10-09
相关资源
最近更新 更多