Подтвердить что ты не робот

Почему GCC реализует isnan() более эффективно для С++ <cmath>, чем C <math.h>?

Здесь мой код:

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> обеспечивает менее эффективную реализацию isnan(), чем заголовок С++ <cmath>? Предполагают ли люди использовать __builtin_isnan(), и если да, то почему?

Я тестировал GCC 4.7.2 и 4.9.0 на x86-64 с оптимизацией -O2 и -O3.

4b9b3361

Ответ 1

Глядя на <cmath> для libstdС++, поставляемого с gcc 4.9, вы получаете следующее:

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

Функция A constexpr может быть агрессивно встроена и, конечно же, функция просто делегирует работу на __builtin_isnan.

Заголовок <math.h> не использует __builtin_isnan, вместо этого он использует реализацию __isnan, которая довольно долго вставляется здесь, но на моей машине ™ строки 430 math.h. Поскольку для стандарта C99 требуется использование макроса для isnan et al (раздел 7.12 стандарта C99), "функция" определяется следующим образом:

#define isnan(x) (sizeof (x) == sizeof (float) ? __isnanf (x)   \
  : sizeof (x) == sizeof (double) ? __isnan (x) \
  : __isnanl (x))

Однако я не вижу причин, по которым он не может использовать __builtin_isnan вместо __isnan, поэтому я подозреваю, что это недосмотр. Как отмечает Марк Глисс в комментариях, есть соответствующий отчет об ошибках для аналогичной проблемы, используя isinf вместо isnan.