funciones - Cómo usar nan e inf en C?
pow c (8)
Tengo un método numérico que podría devolver nan o inf si hubiera un error, y para fines de prueba me gustaría forzarlo temporalmente a devolver nan o inf para garantizar que la situación se maneje correctamente. ¿Existe una manera confiable, independiente del compilador de crear valores de nan e inf en C?
Después de buscar en Google durante aproximadamente 10 minutos, solo he podido encontrar soluciones dependientes del compilador.
Esto funciona tanto para float
como para double
:
double NAN = 0.0/0.0;
double POS_INF = 1.0 /0.0;
double NEG_INF = -1.0/0.0;
Editar: Como alguien dijo, el antiguo estándar IEEE decía que tales valores deberían generar trampas. Pero los nuevos compiladores casi siempre desconectan las trampas y devuelven los valores dados porque la interceptación interfiere con el manejo de errores.
No existe una forma independiente de compilación de esto, ya que ni los estándares C (ni C ++) dicen que los tipos matemáticos de coma flotante deben soportar NAN o INF.
Editar: Acabo de comprobar la redacción del estándar de C ++, y dice que estas funciones (miembros de la clase de plantilla numeric_limits):
quiet_NaN()
signalling_NaN()
Devolverá las representaciones de NAN "si está disponible". No amplía lo que significa "si está disponible", pero presumiblemente algo así como "si el representante de FP de la implementación lo admite". Del mismo modo, hay una función:
infinity()
que devuelve un representante INF positivo "si está disponible".
Ambos están definidos en el encabezado <limits>
- 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.
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; sino a una constante positiva de tipo float que se desborda en el tiempo de traducción. "
NAN
puede definirse o no, y "se define si y solo si la implementación admite NaN silenciosos para el tipo flotante. Se expande a una expresión constante de tipo flotante que representa un NaN silencioso".
Tenga en cuenta que si compara valores de puntos flotantes, haga lo siguiente:
a = NAN;
incluso entonces,
a == NAN;
Es falso. Una forma de comprobar si NaN sería:
#include <math.h>
if (isnan(a)) { ... }
También puede hacer: a != a
para probar si a
es NaN.
También hay isfinite()
, isinf()
, isnormal()
y signbit()
en math.h
en C99.
C99 también tiene funciones nan
:
#include <math.h>
double nan(const char *tagp);
float nanf(const char *tagp);
long double nanl(ocnst char *tagp);
(Referencia: n1256).
También me sorprende que estas no sean constantes de tiempo de compilación. Pero supongo que podría crear estos valores con la suficiente facilidad simplemente ejecutando una instrucción que devuelva un resultado no válido. Dividiendo por 0, log de 0, tan de 90, esa cosa.
Una forma independiente del compilador, pero no 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 use el formato de coma flotante IEEE 754 (que x86 sí).
ACTUALIZACIÓN: Probado y actualizado.
Usualmente uso
#define INFINITY (1e999)
o
const double INFINITY = 1e999
que funciona al menos en contextos IEEE 754 porque el valor doble más alto representable es aproximadamente 1e308
. 1e309
funcionaría igual de bien, como lo haría 1e99999
, pero tres nueves es suficiente y memorable. Como esto es un doble literal (en el caso #define
) o un valor real de Inf
, seguirá siendo infinito incluso si está utilizando flotantes de 128 bits ("doble largo").
<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. */
double a_nan = strtod("NaN", NULL);
double a_inf = strtod("Inf", NULL);