如何正确检查一个值是否为无穷大或在c++(msvc2010)



我几乎100%肯定以前有人问过这个问题,但是我对这个问题的搜索并没有导致一个令人满意的答案。

让我们开始吧。我所有的问题都来自这个小问题:-1.#IND000。所以基本上我的值要么是nan要么是infinite,所以计算会爆炸导致错误。

因为我正在使用浮点数,所以我一直在c#中使用float.IsNan()和float.IsInfinity()但是当我开始用c++编码时,我还没有在c++中找到相当的函数。所以我写了一个模板来检查浮点数是否为nan,如下所示:

template <typename T> bool isnan (T value)
{ return value != value; }

但是我应该如何写一个函数来定义如果浮点数是无限的?毕竟我的检查做得好吗?另外,我在一个定时循环中做检查,所以模板应该动作快。

谢谢你的时间!

您正在寻找std::isnan()std::isinf()。您不应该尝试自己编写这些函数,因为它们是标准库的一部分。

现在,我有一个挥之不去的疑问,这些函数不存在于VS2010附带的标准库中。在这种情况下,您可以通过使用CRT提供的函数来解决遗漏问题。具体来说,float.h中声明的函数有:_isnan()_finite(x)_fpclass()

注意:

  • x是NaN当且仅当x != x
  • x是NaN或无穷大当且仅当x - x != 0
  • x是零或无穷大当且仅当x + x == x
  • x是零当且仅当x == 0
  • 如果FLT_EVAL_METHOD01,则x是无穷大当且仅当x + DBL_MAX == x
  • x是正无穷当且仅当x + infinity == x

我不认为使用上面的比较函数而不是标准库函数有什么错,即使这些标准库函数存在。事实上,在评论中与David Heffernan讨论后,我建议在isinf/isfinite/isnan宏/函数上使用上面的算术比较。

我看到你在这里使用微软编译器。我没有安装。下面的内容都是参考我的Arch盒子上的gcc,即gcc version 4.9.0 20140521 (prerelease) (GCC)完成的,所以这最多是一个可移植性说明。在你的编译器中尝试类似的东西,看看哪些变体(如果有的话)告诉编译器发生了什么,哪些只是让它放弃。

考虑以下代码:

int foo(double x) {
  return x != x;
}
void tva(double x) {
  if (!foo(x)) {
    x += x;
    if (!foo(x)) {
      printf(":(");
    }
  }
}

这里fooisnan的实现。x += x不会产生NaN,除非x之前是NaN。以下是为tva生成的代码:

0000000000000020 <_Z3tvad>:
  20:   66 0f 2e c0             ucomisd %xmm0,%xmm0
  24:   7a 1a                   jp     40 <_Z3tvad+0x20>
  26:   f2 0f 58 c0             addsd  %xmm0,%xmm0
  2a:   66 0f 2e c0             ucomisd %xmm0,%xmm0
  2e:   7a 10                   jp     40 <_Z3tvad+0x20>
  30:   bf 00 00 00 00          mov    $0x0,%edi
  35:   31 c0                   xor    %eax,%eax
  37:   e9 00 00 00 00          jmpq   3c <_Z3tvad+0x1c>
  3c:   0f 1f 40 00             nopl   0x0(%rax)
  40:   f3 c3                   repz retq

注意,没有生成包含printf的分支。如果我们用isnan代替foo会发生什么?

00000000004005c0 <_Z3tvad>:
  4005c0:       66 0f 28 c8             movapd %xmm0,%xmm1
  4005c4:       48 83 ec 18             sub    $0x18,%rsp
  4005c8:       f2 0f 11 4c 24 08       movsd  %xmm1,0x8(%rsp)
  4005ce:       e8 4d fe ff ff          callq  400420 <__isnan@plt>
  4005d3:       85 c0                   test   %eax,%eax
  4005d5:       75 17                   jne    4005ee <_Z3tvad+0x2e>
  4005d7:       f2 0f 10 4c 24 08       movsd  0x8(%rsp),%xmm1
  4005dd:       66 0f 28 c1             movapd %xmm1,%xmm0
  4005e1:       f2 0f 58 c1             addsd  %xmm1,%xmm0
  4005e5:       e8 36 fe ff ff          callq  400420 <__isnan@plt>
  4005ea:       85 c0                   test   %eax,%eax
  4005ec:       74 0a                   je     4005f8 <_Z3tvad+0x38>
  4005ee:       48 83 c4 18             add    $0x18,%rsp
  4005f2:       c3                      retq
  4005f3:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
  4005f8:       bf 94 06 40 00          mov    $0x400694,%edi
  4005fd:       48 83 c4 18             add    $0x18,%rsp
  400601:       e9 2a fe ff ff          jmpq   400430 <printf@plt>
  400606:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)

看来gcc不知道isnan在做什么!它使用printf生成死分支,并生成对isnan的两个单独调用。

我的观点是,使用isnan宏/函数会混淆gcc的值分析。它不知道isnan(x)当且仅当x是NaN。让编译器优化工作通常比为给定原语生成尽可能快的代码重要得多。

最新更新