【问题标题】:Can we rely on op== to binary-compare floating-point values?我们可以依靠 op== 来二进制比较浮点值吗?
【发布时间】:2011-11-08 01:15:11
【问题描述】:

我们都知道(对吗?!)不应该通过测试是否相等来比较浮点值 (operator==)。

但是如果我真的想确定两个floats ab 是否二进制相等?如果不允许它们是 NaN (或其他“特殊值”),这是否“安全”?我可以依靠operator== 以这种方式运行吗?

【问题讨论】:

  • 您对浮点数的二进制比较的建议用途是什么?换句话说,你为什么要这样做?可能有更好的方法来实现您的实际目标。
  • @Michael:我的目标是阅读这个问题的答案,并以此扩展我对我们行业所有工具的知识和理解。
  • @MichaelKohne 除了 OP 提到的目标(这是一个有效且经常被忽略的目标)之外,在许多情况下二进制比较就足够了。您并不总是需要一些花哨的 epsilon 比较,当您只想检查特定的确切值并且由于舍入而不(甚至不应该)关心近似相似的值时(因此我也不喜欢问题的第一句话太多了)。
  • @Christian:我想我概括了一点。
  • 请注意,尽管在很多情况下将== 与浮点数一起使用并不是一个好主意,但也有一些情况是完全合理的;例如,如果您执行f = 0;,则稍后的测试(如if (f == 0))会完全按照它的样子进行;没有不精确或不确定性。所以我认为人们应该采取经常听到的格言“永远不要将带有 == 的浮点数与一粒盐进行比较......(并且不要盲目地重复它)

标签: c++ floating-point


【解决方案1】:

(假设 IEEE-754 表示)差不多,但不完全。如果您可以排除 NaN,您仍然需要处理 +0.0-0.0 具有不同的二进制编码,但比较相等的事实(因为两者都完全为零)。

当然,C++ 不需要 IEEE-754。所以严格来说,所有的赌注都没有了。

如果您想检查编码是否相等,只需使用memcmp(&a, &b, sizeof a)

【讨论】:

  • 这没有考虑扩展精度。看我的回答。
  • @edA-qamort-ora-y:按照我的建议,这将通过使用 memcmp 来解决。
  • memcmp 是否强制使用浮动存储?是否保证优化器最终不会看到寄存器中两个值的比较而只是比较这些值?我希望,鉴于它有一个大小参数,答案是肯定的,但我不会在不测试该假设的情况下编写关键软件。也许它比较了一些不完整的东西。
  • @edA-qamort-ora-y:是的,标准中的“as-if”规则强制行为与存储两个值时会发生的情况无法区分,因为一个值需要存储以便将其地址传递给函数。 (它们实际上不需要存储;例如,可以将它们四舍五入到它们的实际精度,然后复制到通用寄存器中,并在那里作为整数编码进行比较)。
【解决方案2】:

接受的答案忽略了一个非常重要的方面:扩展精度浮点。 CPU 可能正在使用超过您的存储大小的位大小进行计算。如果您使用float 尤其如此,但double 和其他浮点类型也可能如此。

为了说明问题,以下断言实际上可能会失败,具体取决于编译的完成方式和芯片的行为方式。

  void function( float a )
  {
     float b = a / 0.12345;
     assert( b == (a/0.12345) );
  }

现在,在这个简化的示例中,它可能总是会通过,但在很多情况下不会。只需查看GCC Bug 323 并查看有多少缺陷被标记为重复。这种扩展的精度会给很多人带来麻烦,也可能会给您带来麻烦。

如果您需要保证,您需要做一个比较函数,该函数采用两个浮点参数并保证该函数永远不会内联(存储的浮点数不受扩展精度的影响)。也就是说,您必须确保实际存储了这些浮点数。还有一个名为“store-float”的 GCC 选项,我相信它会强制存储,也许它可以在这里用于您的个人功能。

【讨论】:

  • 这真的回答了这个问题吗?如果ab 甚至没有相同数量的位(由于扩展的浮点精度),那么它们是否是二进制相同的问题就有点没有意义了。
  • 你指的选项是-ffloat-store
  • @MSalters,问题是他能否依赖 operator==,所以我的回答是
  • @edA-qamort-ora-y 不,问题是“我们可以依靠operator== 以这种方式运行吗?”,其中“这种方式”是二进制相等。所以 MSalters 有一个点,虽然我不确定这是否真的会使你的答案无效。
  • @ChristianRau,我只是缩短了我的评论,但我明白他的意思是二元相等。 MSalters 是正确的,二进制相等的概念没有意义,因此我的回答暗示以这种方式使用 == 是错误的。也就是说,它不起作用。
【解决方案3】:

要确保浮点值不是 NaN,您可以将其与自身进行比较:

double foo;
// do something with foo
if (foo != foo) {
    std::cout << "Halp! Foo is NaN!";
}

我很确定这是由 IEEE-754 标准保证的。

【讨论】:

  • 是的,我知道如何测试 NaN。不过,这不是问题所在。
  • 您仍然不能使用 operator== 将浮点数与 NaN 进行比较,因为与 NaN 的比较将始终返回 false。您在问题中特别提到了 NaN。因此,您的问题的答案是“不”。
  • 哦,你是对的。我完全误解了你的问题。道歉。
猜你喜欢
  • 2018-01-20
  • 2013-12-30
  • 2020-05-06
  • 1970-01-01
  • 2018-01-27
  • 2016-01-10
  • 1970-01-01
  • 1970-01-01
  • 2013-02-08
相关资源
最近更新 更多