我如何做一个便携式isnan / isinf函数

我一直在Linux平台上使用isinfisnan函数,这是完美的。 但是这在OS-X上不起作用,所以我决定使用std::isinf std::isnan ,它可以在Linux和OS-X上运行。

但是,英特尔编译器不能识别它,我想它是在intel编译器中的一个错误,根据http://software.intel.com/en-us/forums/showthread.php?t=64188

所以现在我只是想避免麻烦,并定义我自己的isinfisnan实现。

有谁知道如何做到这一点?

编辑:

我结束了在我的源代码中做isinf / isnan工作

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

你也可以使用boost来完成这个任务:

 #include <boost/math/special_functions/fpclassify.hpp> // isnan if( boost::math::isnan( ... ) .... ) 

我没有尝试过,但我会想

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

会工作。 感觉就像isinf应该有一个更好的方法,但这应该工作。

据此, 无限很容易检查:

  • 符号= 0或1位表示正/负无穷大。
  • 指数=全部1位。
  • 尾数=全0位。

NaN有点复杂,因为它没有独特的表示:

  • sign = 0或1。
  • 指数=全部1位。
  • 尾数=除了所有0位以外的任何数据(因为所有的0位都代表无穷大)。

下面是双精度浮点运算的例子。 单精度可以类似地写(回想起,指数对于双精度来说是11位,对于单精度是指8位):

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

这个实现非常简单(我从OpenCV头文件中获取 )。 它使用一个相同大小的无符号64位整数的联合,您可能需要正确声明:

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

这适用于Visual Studio 2008:

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

为了安全起见,我推荐使用fpu_error()。 我相信一些数字是用isnan()来获取的,有些用isinf()来获得,而且你需要两个都是安全的。

这里是一些testing代码:

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

这是输出:

 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现在是C ++ 11的一部分,包括在我相信的GCC ++和Apple LLVM中。

现在MSVC ++在<float.h>有一个_isnan函数。

适当的#define#include应该是一个合适的解决方法。

但是,我build议防止nan发生,而不是nan检测 。

那么,理想情况下,您需要等到英特尔修复该错误或提供解决方法:-)

但是如果你想从IEEE754值中检测出NaNInf ,把它映射到一个整数(32位或64位,取决于它是单精度还是双精度),并检查指数位是否都是1.这表示这两种情况。

你可以通过检查尾数的高位来区分NaNInf 。 如果是1,那就是NaN否则是Inf

+/-Inf由符号位决定。

对于单精度(32位值),符号是高位(b31),指数是下8位(加上23位尾数)。 对于双精度,符号仍然是高位,但指数是11位(尾数加52位)。

维基百科有所有的血淋淋的细节。

以下代码显示了编码如何工作。

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

并输出:

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

只要记住,这个代码(但不是方法)很大程度上取决于你的longs的大小是不是太便携。 但是,如果您必须小心翼翼地获取信息,您已经进入该领域:-)

另外,我总是发现Harald Schmidt的IEEE754转换器对于浮点分析非常有用。

正如brubelsabs所说,Boost提供了这个function,但是,正如这里所报告的,而不是使用

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

这应该使用:

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

没有人似乎提到了C99函数fpclassify,它返回:

FP_INFINITE,FP_NAN,FP_NORMAL,FP_SUBNORMAL,FP_ZERO或实现定义的types之一,指定arg的类别。

这与视觉工作室,但我不知道OS-X。

下面的文章有一些有趣的技巧isnan和isinf: http ://jacksondunstan.com/articles/983

只需使用超级简单的IEEE 754-1985兼容代码即可:

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

这在osx上工作

 #include <math.h> 

这也可能是便携式的,

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

编辑:

正如克里斯指出的,以上可能会失败与大x

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