Tengo un método numérico que podría devolver nan o inf si hubiera un error, y para las pruebas me gustaría forzarlo temporalmente a devolver nan o inf para asegurar que la situación se maneje correctamente. ¿Existe una forma confiable e independiente del compilador de crear valores de nan e inf en C?
Después de buscar en Google durante unos 10 minutos, solo he podido encontrar soluciones dependientes del compilador.
Respuestas:
Puede probar si su implementación lo tiene:
#include <math.h> #ifdef NAN /* NAN is supported */ #endif #ifdef INFINITY /* INFINITY is supported */ #endif
La existencia de
INFINITY
está garantizada por C99 (o el último borrador al menos), y "se expande a una expresión constante de tipo float que representa infinito positivo o sin signo, si está disponible; de lo contrario, a una constante positiva de tipo float que se desborda en el momento de la traducción".NAN
puede o no estar definido, y "se define si y solo si la implementación admite NaN silenciosos para el tipo float. Se expande a una expresión constante de tipo float que representa un NaN silencioso".Tenga en cuenta que si está comparando valores de punto flotante y haga lo siguiente:
incluso entonces,
Es falso. Una forma de verificar el NaN sería:
#include <math.h> if (isnan(a)) { ... }
También puede hacer:
a != a
para probar sia
es NaN.También hay
isfinite()
,isinf()
,isnormal()
, ysignbit()
macros enmath.h
en C99.C99 también tiene
nan
funciones:#include <math.h> double nan(const char *tagp); float nanf(const char *tagp); long double nanl(const char *tagp);
(Referencia: n1256).
Docs INFINITY Docs NAN
fuente
a != a
debe NUNCA ser utilizado.a
es un número,a == NAN
que devuelva falso. IEEE lo requiere. Incluso las implementaciones que se adhieren a IEEE, lo hacen en su mayoría . Cuandoisnan()
no está implementado, es mejor ajustar la prueba que codificar directamentea == NAN
.No existe una forma independiente del compilador de hacer esto, ya que ni los estándares C (ni los C ++) dicen que los tipos matemáticos de coma flotante deben admitir NAN o INF.
Editar: Acabo de verificar la redacción del estándar C ++, y dice que estas funciones (miembros de la clase de plantilla numeric_limits):
devolverá las representaciones NAN "si están disponibles". No amplía lo que significa "si está disponible", sino presumiblemente algo como "si el representante de FP de la implementación los admite". Del mismo modo, hay una función:
que devuelve un representante INF positivo "si está disponible".
Ambos están definidos en el
<limits>
encabezado; supongo que el estándar C tiene algo similar (probablemente también "si está disponible"), pero no tengo una copia del estándar C99 actual.fuente
<math.h>
definenan()
,nanf()
ynanl()
que devuelven diferentes representaciones de NaN (comodouble
,float
, yint
respectivamente), y el infinito (si está disponible) podría ser devuelto mediante la generación de una conlog(0)
o algo así. No hay una forma estándar de verificarlos, incluso en C99. El<float.h>
encabezado (<limits.h>
es para tipos integrales) desafortunadamente no habla sobre los valoresinf
ynan
.nanl()
devuelve unlong double
, noint
como dice mi comentario. No sé por qué no me di cuenta de eso cuando lo estaba escribiendo.Esto funciona para ambos
float
ydouble
:double NAN = 0.0/0.0; double POS_INF = 1.0 /0.0; double NEG_INF = -1.0/0.0;
Editar: Como ya dijo alguien, el antiguo estándar IEEE decía que tales valores deberían generar trampas. Pero los nuevos compiladores casi siempre desactivan las trampas y devuelven los valores dados porque la trampa interfiere con el manejo de errores.
fuente
#define is_nan(x) ((x) != (x))
puede resultar útil como prueba sencilla y portátil para NAN.Una forma independiente del compilador, pero no una forma independiente del procesador para obtener estos:
int inf = 0x7F800000; return *(float*)&inf; int nan = 0x7F800001; return *(float*)&nan;
Esto debería funcionar en cualquier procesador que utilice el formato de punto flotante IEEE 754 (que hace x86).
ACTUALIZACIÓN: Probado y actualizado.
fuente
(float &)
? Eso no me parece C. Necesitasint i = 0x7F800000; return *(float *)&i;
0x7f800001
es un llamado NaN de señalización en el estándar IEEE-754. Aunque la mayoría de las bibliotecas y el hardware no admiten la señalización de NaN, es probable que sea mejor devolver un NaN silencioso como0x7fc00000
.double a_nan = strtod("NaN", NULL); double a_inf = strtod("Inf", NULL);
fuente
strtod
y convierte NaN e Inf.<inf.h> /* IEEE positive infinity. */ #if __GNUC_PREREQ(3,3) # define INFINITY (__builtin_inff()) #else # define INFINITY HUGE_VALF #endif
y
<bits/nan.h> #ifndef _MATH_H # error "Never use <bits/nan.h> directly; include <math.h> instead." #endif /* IEEE Not A Number. */ #if __GNUC_PREREQ(3,3) # define NAN (__builtin_nanf ("")) #elif defined __GNUC__ # define NAN \ (__extension__ \ ((union { unsigned __l __attribute__ ((__mode__ (__SI__))); float __d; }) \ { __l: 0x7fc00000UL }).__d) #else # include <endian.h> # if __BYTE_ORDER == __BIG_ENDIAN # define __nan_bytes { 0x7f, 0xc0, 0, 0 } # endif # if __BYTE_ORDER == __LITTLE_ENDIAN # define __nan_bytes { 0, 0, 0xc0, 0x7f } # endif static union { unsigned char __c[4]; float __d; } __nan_union __attribute_used__ = { __nan_bytes }; # define NAN (__nan_union.__d) #endif /* GCC. */
fuente
También me sorprende que estas no sean constantes de tiempo de compilación. Pero supongo que podría crear estos valores con bastante facilidad simplemente ejecutando una instrucción que devuelva un resultado tan inválido. Dividiendo por 0, logaritmo de 0, bronceado de 90, ese tipo de cosas.
fuente
Yo suelo usar
#define INFINITY (1e999)
o
const double INFINITY = 1e999
que funciona al menos en contextos IEEE 754 porque el valor doble representable más alto es aproximadamente
1e308
.1e309
funcionaría igual de bien,1e99999
pero tres nueves son suficientes y memorables. Dado que este es un doble literal (en el#define
caso) o unInf
valor real , seguirá siendo infinito incluso si está utilizando flotantes de 128 bits ("doble largo").fuente
1e999
literales ya no se redondean a+Infinity
. Según las leyes de Murphy, esto rompe un algoritmo. Peor aún: un programador humano que realiza la compilación de "128 bits" probablemente no detectará ese error con anticipación. Es muy probable que sea demasiado tarde cuando se detecte y reconozca este error. Muy peligroso.Aquí hay una forma simple de definir esas constantes, y estoy bastante seguro de que es portátil:
const double inf = 1.0/0.0; const double nan = 0.0/0.0;
Cuando ejecuto este código:
printf("inf = %f\n", inf); printf("-inf = %f\n", -inf); printf("nan = %f\n", nan); printf("-nan = %f\n", -nan);
Yo obtengo:
fuente