如何正确检查值是否为无穷大或 nan ic c++(msvc2010)

Joh*_*ike -1 c++ floating-point templates

我几乎 100% 肯定之前有人问过这个问题,但是我对此的搜索并没有得到令人满意的答案。

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

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

template <typename T> bool isnan (T value)
{ return value != value; }
Run Code Online (Sandbox Code Playgroud)

但是我应该如何编写一个函数来定义浮点数是否为无限?毕竟我的 nan 检查是否正确完成?此外,我正在定时循环中进行 ckecks,因此模板应该快速运行。

谢谢你的时间!

tmy*_*ebu 6

注意:

  • 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//isfiniteisnan宏/函数上使用上面的算术比较。

我发现您在这里使用的是 Microsoft 编译器。我没有安装。下面的内容都是参考gcc我的Arch盒子上的,即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(":(");
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

foo是 的一个实现isnanx += 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
Run Code Online (Sandbox Code Playgroud)

请注意,包含 的分支printf并未生成。foo如果我们替换为 会发生什么isnan

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)
Run Code Online (Sandbox Code Playgroud)

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

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


Dav*_*nan 5

您正在寻找std::isnan()std::isinf()。鉴于这些函数作为标准库的一部分存在,您不应尝试自己编写这些函数。

现在,我一直怀疑这些函数不存在于 VS2010 附带的标准库中。在这种情况下,您可以使用 CRT 提供的功能来解决遗漏问题。具体有以下几种功能中声明float.h_isnan()_finite(x)_fpclass()

  • 不要测试 1 的返回值。文档说他们返回非零值来表示真。测试`!= 0`。我认为 `_fpclass()` 可能对你更有用。 (2认同)