Pregunta Cómo usar nan e inf en C?


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. ¿Hay un confiable, compilador independiente forma 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.


76
2017-12-17 18:57


origen


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á garantizado por C99 (o el último borrador al menos), y "se expande a una expresión constante de tipo flotante que representa positivo o sin signo infinito, si está disponible; sino a una constante positiva de tipo flotante que se desborda en el tiempo de traducción ".

NAN puede o no estar definido, y "se define si y solo si la implementación admite NaN silenciosos para el tipo de letra flotante. Se expande a una expresión constante de tipo flotante que representa un NaN silencioso".

Tenga en cuenta que si está comparando valores de coma flotante, y lo hace:

a = NAN;

incluso entonces,

a == NAN;

Es falso. Una forma de comprobar si NaN sería:

#include <math.h>
if (isnan(a)) { ... }

También puedes hacer: a != a para probar si a es NaN.

También hay isfinite(), isinf(), isnormal()y signbit() macros en math.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(ocnst char *tagp);

(Referencia: n1256).


71
2017-12-17 19:15



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 <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.


33
2017-12-17 19:00



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.


19
2017-12-17 19:08



Esto funciona para ambos float y double:

double NAN = 0.0/0.0;
double POS_INF = 1.0 /0.0;
double NEG_INF = -1.0/0.0;

Editar: Como alguien ya dijo, el antiguo estándar IEEE decía que tales valores deberían levantar trampas. Pero los nuevos compiladores casi siempre apaga las trampas y devuelve el valores dados porque la captura interfiere con el error manejo.


18
2017-12-17 19:26



double a_nan = strtod("NaN", NULL);
double a_inf = strtod("Inf", NULL);

12
2017-08-01 13:07



<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.  */

3
2018-03-04 00:14



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.


0
2017-12-17 19:09



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. Dado que esto es un doble literal (en el #define caso) o un real Inf valor, seguirá siendo infinito incluso si está utilizando flotadores de 128 bits ("doble largo").


0
2017-07-15 22:58