为什么GCC对C ++ <cmath>比C <math.h>更有效地实现isnan()?

这是我的代码:

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

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

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

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

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

 jmp __isnan 

现在代码不是内联的, __isnan函数当然不会快于ucomisd指令,所以我们已经发生了跳跃,没有任何好处。 如果我将代码编译为C,我会得到同样的结果

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

所以我的问题是,为什么C <math.h>头提供了比C ++ <cmath>头更低效率的isnan()实现? 人们是否真的希望使用__builtin_isnan() ,如果是这样,为什么?

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

看看<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标准要求使用isnan等人的macros(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的类似问题上有一个相关的bug报告 。