¿Por qué GCC implementa isnan() de manera más eficiente para C++ <cmath> que para C <math.h>?

 C Programming >> Programación C >  >> Tags >> GCC
¿Por qué GCC implementa isnan() de manera más eficiente para C++ <cmath> que para C <math.h>?


Aquí está mi código:


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

Si yo #include <cmath> Obtengo esta asamblea:


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

Esto es razonablemente inteligente:ucomisd establece el indicador de paridad si la comparación de x consigo mismo no está ordenada, lo que significa que x es NAN. Luego, setp copia el indicador de paridad en el resultado (solo un byte, por lo tanto, el borrado inicial de %eax ).


Pero si yo #include <math.h> Obtengo esta asamblea:


jmp     __isnan

Ahora el código no está en línea y el __isnan La función ciertamente no es más rápida que la ucomisd instrucción, por lo que hemos incurrido en un salto sin ningún beneficio. Obtengo lo mismo si compilo el código como C.


Ahora si cambio el isnan() llamar a __builtin_isnan() , obtengo el simple ucomisd instrucción instrucción independientemente del encabezado que incluya, y también funciona en C. Del mismo modo, si solo return x != x .


Entonces mi pregunta es, ¿por qué C <math.h> encabezado proporciona una implementación menos eficiente de isnan() que el C++ <cmath> ¿encabezamiento? ¿Realmente se espera que la gente use __builtin_isnan()? , y si es así, ¿por qué?


Probé GCC 4.7.2 y 4.9.0 en x86-64 con -O2 y -O3 optimización.


Respuestas:


Mirando <cmath> para libstdc++ enviado con gcc 4.9 obtienes esto:


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

Un constexpr la función podría estar agresivamente en línea y, por supuesto, la función simplemente delega el trabajo a __builtin_isnan .


El <math.h> el encabezado no usa __builtin_isnan , más bien usa un __isnan implementación que es algo larga de pegar aquí pero son las líneas 430 de math.h en mi máquina™. Dado que el estándar C99 requiere el uso de una macro para isnan et al (sección 7.12 del estándar C99), la 'función' se define de la siguiente manera:


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

Sin embargo, no veo ninguna razón por la que no pueda usar __builtin_isnan en lugar de __isnan así que sospecho que es un descuido. Como señala Marc Glisse en los comentarios, hay un informe de error relevante para un problema similar usando isinf en lugar de isnan .