how do I make a portable isnan/isinf function
Solution 1
You could also use boost for this task:
#include <boost/math/special_functions/fpclassify.hpp> // isnan
if( boost::math::isnan( ... ) .... )
Solution 2
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.
Solution 3
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
Solution 4
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.
Solution 5
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 #define
s and #include
s should make a suitable workaround.
However, I recommend preventing nan from ever occurring, instead of nan detection.
monkeyking
Updated on November 23, 2020Comments
-
monkeyking over 3 years
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 usestd::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; }
-
John D. Cook about 14 yearsI've done something like your isnan function and it work on Windows, Linux, and OS X.
-
Eric over 13 yearsYes, bring ~7000 header files to bear on a problem that can be solved with 2 or 3 lines.
-
math over 13 yearsYOU 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?
-
Chris over 13 yearsCan'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)
-
Eamon Nerbonne over 13 years_finite returns false for both inf and nan, so your isinf implementation is incorrect - in fact, your own demo output shows that :-)
-
Emil Styrke about 12 yearsThis (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.aspx
-
aka.nice almost 12 yearsOops, after int isinf( double x ) { return x == x * 2; } edition, you now have isinf(0.0)...
-
Pascal Cuoq over 10 yearsAlso
int isinf(double x) { return fabs(x) > DBL_MAX; }
. -
cmaster - reinstate monica over 10 yearsUnfortunately, the expression
*(((long*)(&x))+1)
invokes undefined behaviour: After the cast of the pointer tolong*
, the compiler is allowed to infer that the resulting pointer is not aliased with the original one, because one points to along
while the other points to adouble
, and perform optimizations on it. This might become a problem when the compiler decides to inlinedecode()
, 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, usememcpy()
instead of a cast. -
Keith Thompson over 10 yearsWhat if
x * 2
overflows? -
pattivacek about 10 yearsSometimes 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.
-
Amro over 8 yearsif 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/… -
Maxim Egorushkin almost 8 years
-ffast-math
breaks this, usestd::isnan
. -
Sheldon Juncker over 7 years+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.
-
plasmacel over 7 yearsThe 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.
-
math over 7 yearsLuckily 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.
-
Pablo H over 5 yearsSometimes you use NaN to indicate "invalid", similar to null_ptr.
-
Niko O over 2 yearsI'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?
-
Amro over 2 years@NikoO I'm not sure either, a quick search turned the following results: stackoverflow.com/q/52290456, stackoverflow.com/q/25664848, stackoverflow.com/q/11373203, en.wikipedia.org/wiki/Type_punning#Use_of_union. An alternative that also seems accepted is using
memcpy
: stackoverflow.com/q/17789928, stackoverflow.com/q/51300626, or using C++20std::bit_cast