how do I make a portable isnan/isinf function
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 i开发者_C百科snan_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;
}
You could also use boost for this task:
#include <boost/math/special_functions/fpclassify.hpp> // isnan
if( boost::math::isnan( ... ) .... )
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.
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
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.
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.
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.
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; }
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))
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.
The following article has some interesting tricks for isnan and isinf: http://jacksondunstan.com/articles/983
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; }
精彩评论