how do I make a portable isnan/isinf function
Asked Answered
L

11

36

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

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?

edit:

I ended up doing this in my source code 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;
}
Lignin answered 12/2, 2010 at 1:40 Comment(1)
Closely related: Checking if a double (or float) is nan in C++Ybarra
I
25

You could also use boost for this task:

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

if( boost::math::isnan( ... ) .... )
Illogic answered 10/9, 2010 at 8:28 Comment(4)
Yes, bring ~7000 header files to bear on a problem that can be solved with 2 or 3 lines.Spacious
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?Illogic
The OP's question is "how do I MAKE a portable isnan/isinf function". It's crystal clear that the OP wants to implement it himself. This answer should be posted as a comment, not an answer. It doesn't answer the question, even worse it recommends to use a huge library.Whereunto
Luckily Boost is open source, so he also may read the implementations to get a guess how this can be solved. As the OP mentions that he has used before predefined isnan isinf functions, he probably does not need to reinvent the wheel. If the size of the lib is really an issue here, which you are ASSUMING!, than other solutions posted here, or Boost, may be helpful. Unless he doesn't make the size an requirement, it is safe to DON'T OPTIMIZE FIRST!. BTW the bug he mentions has been fixed meanwhile.Illogic
V
23

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.

Varick answered 12/2, 2010 at 1:54 Comment(4)
I've done something like your isnan function and it work on Windows, Linux, and OS X.Ponderous
This (x != x) will not work in MSVC or gcc with the fast math flag (i.e. if the floating point implementation does not conform to IEEE). See msdn.microsoft.com/en-us/library/e7s85ffb.aspxCrescendo
Also int isinf(double x) { return fabs(x) > DBL_MAX; }.Selective
-ffast-math breaks this, use std::isnan.Nest
J
18

According to this, infinity is easy to check:

  • sign = either 0 or 1 bit indicating positive/negative infinity.
  • exponent = all 1 bits.
  • mantissa = all 0 bits.

NaN is a bit more complicated because it doesn't have a unique representation:

  • sign = either 0 or 1.
  • exponent = all 1 bits.
  • mantissa = anything except all 0 bits (since all 0 bits represents infinity).

Below is the code for double-precision floating-point case. Single-precision can be similarly written (recall that the exponent is 11-bits for doubles and 8-bits for singles):

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

The implementation is pretty straightforward (I took those from the OpenCV header files). It uses a union over an equal-sized unsigned 64-bit integer which you might need to correctly declare:

#if defined _MSC_VER
  typedef unsigned __int64 uint64;
#else
  typedef uint64_t uint64;
#endif
Jalapa answered 21/12, 2013 at 21:47 Comment(4)
if anyone is interested, that definition in OpenCV moved around, and it's now in the hal module. Here's a more permanent link: github.com/Itseez/opencv/blob/3.0.0/modules/hal/include/opencv2/…Jalapa
+1 This clearly seems to be the most platform-independent approach that is guaranteed to get the correct answer without the need of including libraries and without regard to compiler settings.Eyepiece
I've heard that plonking a value of one type into a union and reading it back as a different type is undefined behavior in C++ (or maybe just C?). Can you elaborate on that?Cobnut
@NikoO I'm not sure either, a quick search turned the following results: https://mcmap.net/q/88340/-is-the-following-c-union-access-pattern-undefined-behavior, https://mcmap.net/q/23993/-is-it-allowed-to-use-unions-for-type-punning-and-if-not-why, https://mcmap.net/q/15754/-accessing-inactive-union-member-and-undefined-behavior, en.wikipedia.org/wiki/Type_punning#Use_of_union. An alternative that also seems accepted is using memcpy : https://mcmap.net/q/23996/-what-39-s-a-proper-way-of-type-punning-a-float-to-an-int-and-vice-versa, https://mcmap.net/q/88341/-is-std-memcpy-between-different-trivially-copyable-types-undefined-behavior, or using C++20 std::bit_castJalapa
A
8

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.
Anther answered 18/7, 2010 at 12:42 Comment(1)
_finite returns false for both inf and nan, so your isinf implementation is incorrect - in fact, your own demo output shows that :-)Nave
Y
7

isnan is part of C++11 now, included in GCC++ I believe, and Apple LLVM.

Now MSVC++ has an _isnan function in <float.h>.

Appropriate #defines and #includes should make a suitable workaround.

However, I recommend preventing nan from ever occurring, instead of nan detection.

Ybarra answered 15/6, 2013 at 18:53 Comment(2)
Sometimes you can't control if there are nans in your data, especially if you didn't originate the data, and the output still needs to be aware of those nans.Theca
Sometimes you use NaN to indicate "invalid", similar to null_ptr.Seedman
D
6

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.

Devlin answered 12/2, 2010 at 1:54 Comment(1)
Unfortunately, the expression *(((long*)(&x))+1) invokes undefined behaviour: After the cast of the pointer to long*, the compiler is allowed to infer that the resulting pointer is not aliased with the original one, because one points to a long while the other points to a double, and perform optimizations on it. This might become a problem when the compiler decides to inline decode(), since it would allow the compiler to move the integer read in front of the floating point write, which obviously would produce garbage. To be safe, use memcpy() instead of a cast.Flop
M
3

Just use that super simple IEEE 754-1985-compliant code:

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; }
Mareah answered 22/11, 2016 at 12:32 Comment(0)
C
2

As brubelsabs said Boost offers this feature but, as reported here, instead of using

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

This should be used:

if ((boost::math::isnan)(number))
Comradery answered 6/4, 2011 at 12:43 Comment(0)
V
1

No-one seems to have mentioned the C99 function fpclassify which returns:

One of FP_INFINITE, FP_NAN, FP_NORMAL, FP_SUBNORMAL, FP_ZERO or implementation-defined type, specifying the category of arg.

This works with visual studio, but I don't know about OS-X.

Viscardi answered 21/6, 2016 at 8:53 Comment(0)
B
0

The following article has some interesting tricks for isnan and isinf: http://jacksondunstan.com/articles/983

Barium answered 21/9, 2011 at 20:58 Comment(0)
L
-3

this works on osx

#include <math.h>

also this might be portable,

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

edit:

as Chris pointed out the above may fail with large x

int isinf( double x ) { return x == x * 2; }
Lapp answered 23/4, 2010 at 5:12 Comment(3)
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)Belongings
Oops, after int isinf( double x ) { return x == x * 2; } edition, you now have isinf(0.0)...Clash
What if x * 2 overflows?Tacky

© 2022 - 2024 — McMap. All rights reserved.