tags:

views:

514

answers:

5

I've been using isinf,isnan functions on linux platforms which worked perfectly. But this didn't work on osx, so I decided to use std::isinf std::isnan which works on both linux and osx.

But the intel compiler doesn't recognize it, and I guess its a bug in the intel compiler according to http://software.intel.com/en-us/forums/showthread.php?t=64188

So now I just want to avoid the hassle and define my own isinf,isnan implementation.

Does anyone know how this could be done

Thanks

edit: I ended up doing this in my sourcecode for making isinf/isnan working

#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;
}
+5  A: 

I've not tried this, but I would think

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

would work. It feels like there should be a better way for isinf, but that should work.

Johann Hibschman
I've done something like your isnan function and it work on Windows, Linux, and OS X.
John D. Cook
This doesn't work with the intel compiler
monkeyking
+2  A: 

Well, ideally, you'd wait until Intel fixes the bug or provides a workaround :-)

But if you want to detect NaN and Inf from IEEE754 values, map it to an integer (32 or 64 bit depending on whether it's single or double precision) and check if the exponent bits are all 1. This indicates those two cases.

You can distinguish between NaN and Inf by checking the high order bit of the mantissa. If it's 1, that's NaN otherwise Inf.

+/-Inf is dictated by the sign bit.

For single precision (32-bit values), the sign is the high-order bit (b31), exponent is the next eight bits (plus a 23-bit mantissa). For double precision, the sign is still the high-order bit but the exponent is eleven bits (plus 52 bits for the mantissa).

Wikipedia has all the gory details.

The following code shows you how the encoding works.

#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;
}

and it outputs:

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

Just keep in mind that this code (but not the method) depends a great deal on the sizes of your longs which is not overly portable. But, if you have to bit-fiddle to get the information, you've already entered that territory :-)

As an aside, I've always found Harald Schmidt's IEEE754 converter very useful for floating point analysis.

paxdiablo
A: 

this works on osx

#include <math.h>

also this might be portable,

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

drawnonward
Can't this give you the wrong answer sometimes? If x is sufficiently large, it doesn't record the number with integer precision. (i.e. 1.2345*2^100 - 1 == 1.2345*2^100, but 1.2345*2^100 != infinity)
Chris
+1  A: 

This works under Visual Studio 2008:

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

For safety, I recommend using fpu_error(). I believe some numbers are picked up with isnan(), and some with isinf(), and you need both to be safe.

Here is some test code:

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));

Here is the output:

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.
Gravitas
A: 

You could also use boost for this task:

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

if( boost::math::isnan( ... ) .... )
brubelsabs
Yes, bring ~7000 header files to bear on a problem that can be solved with 2 or 3 lines.
Eric
YOU don't have to use it, but if someone nevertheless uses boost anyway, this is a quite PORTABLE and SHORT solution. Without #IFDEFs around, heh?
brubelsabs