为什么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报告 。