2014-09-26 2 views
42

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

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

Если я #include <cmath> я получаю эту сборку:

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

Это достаточно умное: ucomisd устанавливает флаг четности, если сравнение х с самим собой неупорядоченным, что означает х это NAN. Затем setp копирует флаг четности в результат (только один байт, следовательно, начальный сброс %eax).

Но если I #include <math.h> я получаю эту сборку:

jmp  __isnan 

Теперь код не является встроенным, а функция __isnan, конечно, не быстрее Поручение ucomisd, поэтому мы понесли прыгать никакой пользы. Я получаю то же самое, если я компиляции кода, как C.

Теперь, если я изменю isnan() вызов __builtin_isnan(), я получаю простую инструкцию ucomisd инструкции независимо от заголовка включаю, и он работает в C тоже. Точно так же, если я просто return x != x.

Итак, мой вопрос: почему заголовок C <math.h> обеспечивает менее эффективную реализацию isnan(), чем заголовок C++ <cmath>? Действительно ли люди должны использовать __builtin_isnan(), и если да, то почему?

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

+2

вот моя спекуляция: pre c99, нет встроенной функции в c. никакая встроенная функция означает, что функции должны быть вызваны jmp/call (или каким-то разветвлением). __builtin_isnan не входит в c. это, вероятно, определенная платформа. – thang

+2

Но, конечно, системный заголовок, например '', может использовать встроенные встроенные платформы. –

+1

Я уверен, что 'isnan' будет использовать' __builtin_isnan', если это возможно. Я не вижу причин, почему вам придется называть это вручную. – Rapptz

ответ

15

Глядя на <cmath> для libstdC++ поставляется с GCC 4.9 вы получите это:

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

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

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

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

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

+0

на самом деле, ошибка об isinf. это аналогичная проблема с другой функцией, но это не совсем та же проблема. – thang

+2

Не забудьте указать [this] (http://chat.stackoverflow.com/transcript/message/19109073#19109073), говоря, что стандарт * требует *, чтобы они были макросами. – Mysticial

+1

Как вы думаете, было бы правомерно изменять '' просто сказать '#define isnan (x) __builtin_isnan (x)'? –