45

这是我的代码:

int f(double x)
{
  return isnan(x);
}

如果我#include <cmath>得到这个程序集:

xorl    %eax, %eax
ucomisd %xmm0, %xmm0
setp    %al

这是相当聪明的:如果 x 与自身的比较是无序的,则ucomisd设置奇偶校验标志,这意味着 x 是 NAN。然后setp将奇偶校验标志复制到结果中(只有一个字节,因此初始清除%eax)。

但如果我#include <math.h>得到这个程序集:

jmp     __isnan

现在代码没有内联了,__isnan函数肯定没有快了ucomisd指令快,所以我们进行了一次跳转,没有任何好处。如果我将代码编译为 C,我会得到同样的结果。

现在,如果我将isnan()调用更改为,无论我包含哪个标头__builtin_isnan(),我都会得到简单的指令指令,并且它也可以在 C 中使用。ucomisd同样,如果我只是return x != x.

所以我的问题是,为什么 C<math.h>头文件提供的实现效率isnan()低于 C++<cmath>头文件?人们真的期望使用__builtin_isnan(),如果是,为什么?

我在 x86-64 上使用-O2-O3优化测试了 GCC 4.7.2 和 4.9.0。

4

1 回答 1

19

查看<cmath>gcc 4.9 附带的 libstdc++,您会得到:

  constexpr bool
  isnan(double __x)
  { return __builtin_isnan(__x); }

一个constexpr函数可以被积极内联,当然,该函数只是将工作委托给__builtin_isnan.

<math.h>头不使用__builtin_isnan,而是使用__isnan一种在此处粘贴有点长的实现,但它math.h在我的机器™ 上是第 430 行。由于 C99 标准要求使用宏 for isnanet al(C99 标准的第 7.12 节),因此“函数”定义如下:

#define isnan(x) (sizeof (x) == sizeof (float) ? __isnanf (x)   \
  : sizeof (x) == sizeof (double) ? __isnan (x) \
  : __isnanl (x))

但是,我看不出它为什么不能使用__builtin_isnan__isnan所以我怀疑这是一个疏忽。正如 Marc Glisse 在评论中指出的那样,有一个类似问题的相关错误报告,使用isinf而不是isnan.

于 2014-09-26T06:17:55.550 回答