如何正确检查值是无限还是纳米c ++(msvc2010)

时间:2014-06-06 10:25:11

标签: c++ templates floating-point

我几乎100%肯定以前曾经问过这个问题,但我对此的搜索并没有带来满意的感谢。

让我们开始吧。我的所有问题都来自这个小问题:-1。#IND000。 所以基本上我的价值是纳米或无限,因此计算结果爆炸导致错误。

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

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

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

谢谢你的时间!

2 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:0)

请注意:

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

我认为使用上述比较代替标准库函数没有任何问题,即使存在这些标准库函数也是如此。事实上,在与David Heffernan在评论中讨论之后,我建议在isinf / isfinite / isnan宏/函数上使用上面的算术比较。

我看到你在这里使用的是Microsoft编译器。我没有安装。以下内容全部参考了我的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除非x之前是NaN,否则不会产生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的分支。如果我们将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)

gcc似乎不知道isnan做了什么!它使用printf生成死分支,并生成两个单独的isnan调用。

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