这是我的代码:
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()
,无论我包含哪个标头,都会得到简单的ucomisd
指令,并且它也适用于C.同样,如果我只是return x != x
。
所以我的问题是,为什么C <math.h>
标头提供的isnan()
实现效率低于C ++ <cmath>
标头?人们真的希望使用__builtin_isnan()
,如果是,为什么?
我在x86-64上通过-O2
和-O3
优化测试了GCC 4.7.2和4.9.0。
答案 0 :(得分:17)
查看gcc 4.9附带的libstdc ++的<cmath>
,你可以得到:
constexpr bool
isnan(double __x)
{ return __builtin_isnan(__x); }
可以积极地内联constexpr
函数,当然,该函数只是将工作委托给__builtin_isnan
。
<math.h>
标题不使用__builtin_isnan
,而是使用__isnan
实现,这种实现有点长,但是它的第430行{ {1}}在我的机器上。由于C99标准要求使用math.h
等的宏(C99标准的第7.12节),因此&#39;功能&#39;定义如下:
isnan
但是,我认为没有理由不能使用#define isnan(x) (sizeof (x) == sizeof (float) ? __isnanf (x) \
: sizeof (x) == sizeof (double) ? __isnan (x) \
: __isnanl (x))
代替__builtin_isnan
,所以我怀疑它是一种疏忽。正如Marc Glisse在评论中指出的那样,使用__isnan
代替isinf
的问题存在relevant bug report。