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

Posted

技术标签:

【中文标题】为啥 GCC 为 C++ <cmath> 实现 isnan() 比 C <math.h> 更有效?【英文标题】:Why does GCC implement isnan() more efficiently for C++ <cmath> than C <math.h>?为什么 GCC 为 C++ <cmath> 实现 isnan() 比 C <math.h> 更有效? 【发布时间】:2014-11-21 01:21:12 【问题描述】:

这是我的代码:

int f(double x)

  return isnan(x);

如果我 #include &lt;cmath&gt; 我得到这个程序集:

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

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

但如果我 #include &lt;math.h&gt; 我得到这个程序集:

jmp     __isnan

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

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

所以我的问题是,为什么 C &lt;math.h&gt; 标头提供的 isnan() 实现效率低于 C++ &lt;cmath&gt; 标头?人们真的希望使用__builtin_isnan(),如果是,为什么?

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

【问题讨论】:

这是我的推测:c99之前,c中没有内联函数。没有内联函数意味着函数必须由 jmp/call(或某种分支)调用。 __builtin_isnan 不是 c 的一部分。它可能是特定于平台的内在因素。 但像&lt;math.h&gt; 这样的系统头文件肯定可以使用特定于平台的内置插件。 我很确定isnan 会尽可能使用__builtin_isnan。我认为您没有理由必须手动调用它。 也许当 C99 出现时,没有人想过要回去更新 isnan sourceware.org/bugzilla/show_bug.cgi?id=15367 【参考方案1】:

查看&lt;cmath&gt; 与 gcc 4.9 一起提供的 libstdc++,你会得到:

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

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

&lt;math.h&gt; 标头不使用__builtin_isnan,而是使用__isnan 实现,在此处粘贴有点长,但它在我的机器™ 上是math.h 的第430 行。由于 C99 标准要求为 isnan 等人使用宏(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 在 cmets 中指出的那样,有一个类似问题的 relevant bug report 使用 isinf 而不是 isnan

【讨论】:

其实这个bug是关于isinf的。这是一个不同功能的类似问题,但严格来说不是同一个问题。 不要忘记包含this,表示标准要求它们是宏。 您认为将&lt;math.h&gt; 改为简单地说#define isnan(x) __builtin_isnan(x) 是否合法? @JohnZwinck 是的。我想不出它不应该有效的任何理由。 对于 gcc,它是有效的。 __builtin_isnan 并非在每个编译器上都有。

以上是关于为啥 GCC 为 C++ <cmath> 实现 isnan() 比 C <math.h> 更有效?的主要内容,如果未能解决你的问题,请参考以下文章

C++ (macOS) 的编译错误

c++运行结果为啥出现NAN

为啥在此 C++ 代码中出现分段错误?

为啥某些 LLVM 标准库标头包含其他标头但 GCC 不 [重复]

为啥我可以在 C++ 中定义这个可变长度数组? [复制]

C++中的<math>和<cmath>有啥区别