2010-02-12 13 views
36

He estado usando isinf, isnan funciones en plataformas Linux que funcionó a la perfección. Pero esto no funcionó en OS-X, así que decidí usar std::isinfstd::isnan que funciona tanto en Linux como en OS-X.cómo puedo hacer una función isnan/isinf portátil

Pero el compilador Intel no lo reconoce, y supongo que es un error en el compilador Intel acuerdo con http://software.intel.com/en-us/forums/showthread.php?t=64188

Así que ahora sólo quiero evitar la molestia y definir mi propia isinf, isnan aplicación.

¿Alguien sabe cómo esto podría hacerse?

edición:

que terminé haciendo esto en mi código fuente para hacer isinf/isnan trabajo

#include <iostream> 
#include <cmath> 

#ifdef __INTEL_COMPILER 
#include <mathimf.h> 
#endif 

int isnan_local(double x) { 
#ifdef __INTEL_COMPILER 
    return isnan(x); 
#else 
    return std::isnan(x); 
#endif 
} 

int isinf_local(double x) { 
#ifdef __INTEL_COMPILER 
    return isinf(x); 
#else 
    return std::isinf(x); 
#endif 
} 


int myChk(double a){ 
    std::cerr<<"val is: "<<a <<"\t"; 
    if(isnan_local(a)) 
    std::cerr<<"program says isnan"; 
    if(isinf_local(a)) 
    std::cerr<<"program says isinf"; 
    std::cerr<<"\n"; 
    return 0; 
} 

int main(){ 
    double a = 0; 
    myChk(a); 
    myChk(log(a)); 
    myChk(-log(a)); 
    myChk(0/log(a)); 
    myChk(log(a)/log(a)); 

    return 0; 
} 
+1

Estrechamente relacionada: [__Checking si un doble (o flotación) es NaN en C++ __] (http: // stackoverflow. com/questions/570669/checking-if-a-double-or-float-is-nan-in-c) – bobobobo

Respuesta

20

no he intentado esto, pero yo creo

int isnan(double x) { return x != x; } 
int isinf(double x) { return !isnan(x) && isnan(x - x); } 

trabajaría. Se siente como que debería haber una mejor manera para isinf, pero eso debería funcionar.

+0

He hecho algo así como su función isnan y funciona en Windows, Linux y OS X. –

+0

This no funciona con el compilador de intel – monkeyking

+15

Este (x! = x) no funcionará en MSVC o gcc con el indicador matemático rápido (es decir, si la implementación de punto flotante no se ajusta a IEEE). Consulte http://msdn.microsoft.com/en-us/library/e7s85ffb.aspx –

6

Bueno, Lo ideal sería esperar a que Intel corrige el error o proporciona una solución :-)

Pero si desea detectar NaN y Inf de los valores IEEE754, asignarlo a un entero (32 o 64 bit dependiendo de si es de precisión simple o doble) y verificar si los bits de exponente son todos 1. Esto indica esos dos casos.

Puede distinguir entre NaN y Inf comprobando el bit de orden superior de la mantisa. Si es 1, eso es NaN de lo contrario Inf.

+/-Inf está dictada por el bit de signo.

Para la precisión simple (valores de 32 bits), el signo es el bit de orden superior (b31), el exponente son los siguientes ocho bits (más una mantisa de 23 bits). Para la precisión doble, el signo sigue siendo el bit de orden superior, pero el exponente es de once bits (más 52 bits para la mantisa).

Wikipedia tiene todos los detalles sangrientos.

El siguiente código muestra cómo funciona la codificación.

#include <stdio.h> 

static void decode (char *s, double x) { 
    long y = *(((long*)(&x))+1); 

    printf("%08x ",y); 
    if ((y & 0x7ff80000L) == 0x7ff80000L) { 
     printf ("NaN (%s)\n", s); 
     return; 
    } 
    if ((y & 0xfff10000L) == 0x7ff00000L) { 
     printf ("+Inf (%s)\n", s); 
     return; 
    } 
    if ((y & 0xfff10000L) == 0xfff00000L) { 
     printf ("-Inf (%s)\n", s); 
     return; 
    } 
    printf ("%e (%s)\n", x, s); 
} 

int main (int argc, char *argv[]) { 
    double dvar; 

    printf ("sizeof double = %d\n", sizeof(double)); 
    printf ("sizeof long = %d\n", sizeof(long)); 

    dvar = 1.79e308; dvar = dvar * 10000; 
    decode ("too big", dvar); 

    dvar = -1.79e308; dvar = dvar * 10000; 
    decode ("too big and negative", dvar); 

    dvar = -1.0; dvar = sqrt(dvar); 
    decode ("imaginary", dvar); 

    dvar = -1.79e308; 
    decode ("normal", dvar); 

    return 0; 
} 

y se da salida:

sizeof double = 8 
sizeof long = 4 
7ff00000 +Inf (too big) 
fff00000 -Inf (too big and negative) 
fff80000 NaN (imaginary) 
ffefdcf1 -1.790000e+308 (normal) 

sólo tener en cuenta que este código (pero no el método) depende en gran medida de los tamaños de los productos largos, que no es excesivamente portátil. Pero, si tiene que manipular un bit para obtener la información, ya ha ingresado ese territorio :-)

Como un lado, siempre he encontrado Harald Schmidt's IEEE754 converter muy útil para el análisis de coma flotante.

+2

Desafortunadamente, la expresión '* (((largo *) (& x)) + 1)' invoca un comportamiento indefinido: después del molde del puntero a 'long *', el compilador puede inferir que el puntero resultante no tiene alias con el original, porque uno apunta a un "largo" mientras que el otro apunta a un "doble", y realiza optimizaciones en él. Esto podría convertirse en un problema cuando el compilador decida en línea 'decode()', ya que permitiría al compilador mover el entero leído delante de la escritura de coma flotante, lo que obviamente produciría basura. Para estar seguro, use 'memcpy()' en lugar de un molde. – cmaster

-1

esto funciona en OSX

#include <math.h> 

también esto podría ser portátil,

int isinf(double x) { return x == x - 1; } 

edición:

como Chris señaló lo anterior puede fallar con gran x

int isinf(double x) { return x == x * 2; } 
+2

¿No puede esto darle la respuesta incorrecta a veces? Si x es suficientemente grande, no registra el número con precisión entera. (es decir, 1.2345 * 2^100 - 1 == 1.2345 * 2^100, pero 1.2345 * 2^100! = infinito) – Chris

+2

Vaya, después de int isinf (doble x) {return x == x * 2; } edición, ahora tiene isinf (0.0) ... –

+0

¿Qué pasa si 'x * 2' se desborda? –

7

Esto funciona en Visual Studio 2008:

#include <math.h> 
#define isnan(x) _isnan(x) 
#define isinf(x) (!_finite(x)) 
#define fpu_error(x) (isinf(x) || isnan(x)) 

Para mayor seguridad, recomiendo usar fpu_error(). Creo que algunos números se recogen con isnan(), y algunos con isinf(), y necesitas ambos para estar seguros.

Aquí hay un código de prueba:

double zero=0; 
double infinite=1/zero; 
double proper_number=4; 
printf("isinf(infinite)=%d.\n",isinf(infinite)); 
printf("isinf(proper_number)=%d.\n",isinf(proper_number)); 
printf("isnan(infinite)=%d.\n",isnan(infinite)); 
printf("isnan(proper_number)=%d.\n",isnan(proper_number)); 

double num=-4; 
double neg_square_root=sqrt(num); 
printf("isinf(neg_square_root)=%d.\n",isinf(neg_square_root)); 
printf("isinf(proper_number)=%d.\n",isinf(proper_number)); 
printf("isnan(neg_square_root)=%d.\n",isnan(neg_square_root)); 
printf("isnan(proper_number)=%d.\n",isnan(proper_number)); 

Aquí está la salida:

isinf(infinite)=1. 
isinf(proper_number)=0. 
isnan(infinite)=0. 
isnan(proper_number)=0. 
isinf(neg_square_root)=1. 
isinf(proper_number)=0. 
isnan(neg_square_root)=1. 
isnan(proper_number)=0. 
+4

_finite devuelve false para inf * y * nan, por lo que su implementación isinf es incorrecta; de hecho, su propia demostración muestra que :-) –

+0

Es bueno saberlo - ¡gracias! – Contango

25

También es posible usar impulso para esta tarea:

#include <boost/math/special_functions/fpclassify.hpp> // isnan 

if(boost::math::isnan(...) ....) 
+38

Sí, trae ~ 7000 archivos de encabezado para abordar un problema que se puede resolver con 2 o 3 líneas. – Eric

+31

NO es necesario que lo use, pero si alguien usa boost de todos modos, esta es una solución bastante PORTABLE y CORTA. Sin #IFDEF alrededor, ¿eh? – math

+1

La pregunta del OP es "¿CÓMO PUEDO HACER una función isnan/isinf portátil?". Está claro que el OP quiere implementarlo él mismo. Esta respuesta debe publicarse como un comentario, no como una respuesta. No responde la pregunta, y lo que es peor, recomienda usar una gran biblioteca. – plasmacel

2

Como dijo brubelsabs ofertas Boost esta característica pero, como se informó here, en lugar de usar

if (boost::math::isnan(number)) 

Esto se debe utilizar:

if ((boost::math::isnan)(number)) 
13

Según this, infinito es fácil de comprobar:

  • signo = 0 o 1 bit que indica el infinito positivo/negativo.
  • exponente = todos los 1 bits.
  • mantissa = todos los 0 bits.

NaN es un poco más complicado, ya que no tiene una representación única:

  • signo = 0 o 1.
  • exponente = todos los 1 bits.
  • mantissa = cualquier cosa excepto todos los 0 bits (ya que los 0 bits representan infinito).

A continuación se muestra el código para la caja de doble punto de precisión flotante. De precisión simple se puede escribir de manera similar (Recordamos que el exponente es de 11-bits para los dobles y 8-bits para los solteros):

int isinf(double x) 
{ 
    union { uint64 u; double f; } ieee754; 
    ieee754.f = x; 
    return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 && 
      ((unsigned)ieee754.u == 0); 
} 

int isnan(double x) 
{ 
    union { uint64 u; double f; } ieee754; 
    ieee754.f = x; 
    return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) + 
      ((unsigned)ieee754.u != 0) > 0x7ff00000; 
} 

La aplicación es bastante sencillo (Tomé los del OpenCV header files). Utiliza una unión más de un entero de 64 bits sin signo de igual tamaño, que puede que tenga que declarar correctamente:

#if defined _MSC_VER 
    typedef unsigned __int64 uint64; 
#else 
    typedef uint64_t uint64; 
#endif 
+1

si alguien está interesado, esa definición en OpenCV se movió, y ahora está en el módulo 'hal'. Aquí hay un enlace más permanente: https://github.com/Itseez/opencv/blob/3.0.0/modules/hal/include/opencv2/hal/defs.h#L447 – Amro

+2

+1 Esto claramente parece ser la plataforma más -un enfoque independiente que garantiza obtener la respuesta correcta sin la necesidad de incluir bibliotecas y sin tener en cuenta la configuración del compilador. –

1

Nadie parece haber mencionado el fpclassify función C99 que devuelve:

Uno de FP_INFINITE , FP_NAN, FP_NORMAL, FP_SUBNORMAL, FP_ZERO o tipo definido por la implementación, especificando la categoría de arg.

Esto funciona con Visual Studio, pero no sé acerca de OS-X.

0

sólo uso que super simple código IEEE 754-1985 compatible:

static inline bool ISINFINITE(float a)   { return (((U32&) a) & 0x7FFFFFFFU) == 0x7F800000U; } 
static inline bool ISINFINITEPOSITIVE(float a) { return (((U32&) a) & 0xFFFFFFFFU) == 0x7F800000U; } 
static inline bool ISINFINITENEGATIVE(float a) { return (((U32&) a) & 0xFFFFFFFFU) == 0xFF800000U; } 
static inline bool ISNAN(float a)    { return !ISINFINITE(a) && (((U32&) a) & 0x7F800000U) == 0x7F800000U; } 
static inline bool ISVALID(float a)    { return (((U32&) a) & 0x7F800000U) != 0x7F800000U; } 
Cuestiones relacionadas