web-dev-qa-db-ja.com

GCCがC <math.h>よりもC ++ <cmath>に対してisnan()をより効率的に実装するのはなぜですか?

これが私のコードです:

_int f(double x)
{
  return isnan(x);
}
_

_#include <cmath>_の場合、このアセンブリを取得します。

_xorl    %eax, %eax
ucomisd %xmm0, %xmm0
setp    %al
_

これはかなり賢い方法です。 comisd xとそれ自体の比較が順序付けられていない場合、つまりxがNANの場合、パリティフラグを設定します。次に setp は、パリティフラグを結果にコピーします(1バイトのみ、したがって、最初に_%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()を使用することを期待されていますか?もしそうなら、なぜですか?

_-O2_および_-O3_最適化を使用してx86-64でGCC4.7.2および4.9.0をテストしました。

42
John Zwinck

Gcc4.9に同梱されているlibstdc ++の<cmath>を見ると、次のようになります。

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

constexpr関数は積極的にインライン化することができ、もちろん、関数は作業を__builtin_isnanに委任するだけです。

<math.h>ヘッダーは__builtin_isnanを使用せず、ここに貼り付けるのに少し長い__isnan実装を使用しますが、私のマシン™ではmath.hの430行目です。 C99標準ではisnan et 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を使用した同様の問題については、 関連するバグレポート があります。

17
Rapptz