如何制作便携式 isnan/isinf 函数

Posted

技术标签:

【中文标题】如何制作便携式 isnan/isinf 函数【英文标题】:how do I make a portable isnan/isinf function 【发布时间】:2011-01-15 23:41:09 【问题描述】:

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

但英特尔编译器无法识别它,根据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;

【问题讨论】:

密切相关:Checking if a double (or float) is nan in C++ 【参考方案1】:

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

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

if( boost::math::isnan( ... ) .... )

【讨论】:

是的,带上大约 7000 个头文件来解决可以用 2 或 3 行解决的问题。 你不必使用它,但如果有人仍然使用 boost,这是一个非常便携和短的解决方案。没有#IFDEF,嘿? OP 的问题是“我如何制作一个可移植的 isnan/isinf 函数”。很明显,OP 想要自己实现它。这个答案应该作为评论发布,而不是答案。它没有回答问题,更糟糕的是它建议使用一个巨大的库。 幸运的是 Boost 是开源的,所以他也可以阅读实现来猜测如何解决这个问题。正如 OP 提到他在预定义的 isnan isinf 函数之前使用过的那样,他可能不需要重新发明***。如果库的大小在这里确实是一个问题,那么您正在假设!,那么这里发布的其他解决方案或 Boost 可能会有所帮助。除非他不要求尺寸,否则不要先优化是安全的!顺便说一句,他提到的错误已同时修复。【参考方案2】:

我没有尝试过,但我想

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

会工作的。感觉应该有更好的 isinf 方法,但这应该可行。

【讨论】:

我已经完成了类似你的 isnan 函数的操作,它可以在 Windows、Linux 和 OS X 上运行。 这个 (x != x) 在 MSVC 或带有快速数学标志的 gcc 中不起作用(即,如果浮点实现不符合 IEEE)。见msdn.microsoft.com/en-us/library/e7s85ffb.aspx 还有int isinf(double x) return fabs(x) &gt; DBL_MAX; -ffast-math 打破这个,使用std::isnan【参考方案3】:

根据this,infinity 很容易检查:

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

NaN 有点复杂,因为它没有唯一的表示:

符号 = 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 header files 获取了那些)。它在一个大小相等的无符号 64 位整数上使用联合,您可能需要正确声明:

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

【讨论】:

如果有人感兴趣,OpenCV 中的定义会移动,现在位于hal 模块中。这是一个更永久的链接:github.com/Itseez/opencv/blob/3.0.0/modules/hal/include/opencv2/… +1 这显然是最独立于平台的方法,可以保证在不需要包含库且不考虑编译器设置的情况下获得正确答案。【参考方案4】:

这适用于 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() 提取的,你需要两者都是安全的。

这是一些测试代码:

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.

【讨论】:

_finite 对 inf nan 都返回 false,因此您的 isinf 实现不正确 - 事实上,您自己的演示输出表明 :-)【参考方案5】:

isnan 现在是 C++11 的一部分,我相信它包含在 GCC++ 和 Apple LLVM 中。

现在MSVC++ has an _isnan function in &lt;float.h&gt;.

适当的#defines 和#includes 应该是一个合适的解决方法。

不过,我推荐preventing nan from ever occurring, instead of nan detection。

【讨论】:

有时你无法控制你的数据中是否有 nans,尤其是如果你没有创建数据,并且输出仍然需要知道这些 nans。 有时用NaN表示“无效”,类似于null_ptr。【参考方案6】:

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

但是,如果您想从 IEEE754 值中检测 NaNInf,请将其映射到一个整数(32 位或 64 位,具体取决于它是单精度还是双精度)并检查指数位是否全为 1。这个表示这两种情况。

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

+/-Inf 由符号位决定。

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

Wikipedia 拥有所有血淋淋的细节。

以下代码向您展示了编码的工作原理。

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

请记住,此代码(但不是方法)在很大程度上取决于您的 long 的大小,而这不是过度可移植的。但是,如果您不得不费劲儿地获取信息,那么您已经进入了那个领域:-)

顺便说一句,我一直发现Harald Schmidt's IEEE754 converter 对浮点分析非常有用。

【讨论】:

不幸的是,表达式*(((long*)(&amp;x))+1) 调用了未定义的行为:在将指针转换为long* 之后,允许编译器推断结果指针与原始指针没有别名,因为一个指向long,而另一个指向double,并对其进行优化。当编译器决定内联decode() 时,这可能会成为一个问题,因为它允许编译器将整数读取移动到浮点写入之前,这显然会产生垃圾。为了安全起见,请使用memcpy() 而不是演员表。【参考方案7】:

只需使用符合 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; 

【讨论】:

【参考方案8】:

正如 brubelsabs 所说,Boost 提供了此功能,但据报道 here,而不是使用

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

应该使用这个:

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

【讨论】:

【参考方案9】:

似乎没有人提到过返回的 C99 函数 fpclassify:

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

这适用于 Visual Studio,但我不了解 OS-X。

【讨论】:

【参考方案10】:

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

【讨论】:

【参考方案11】:

这适用于 osx

#include <math.h>

这也可能是便携式的,

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

编辑:

正如 Chris 指出的那样,上述方法可能会因 x 大而失败

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

【讨论】:

这不能给你错误的答案吗?如果 x 足够大,它不会以整数精度记录数字。 (即 1.2345*2^100 - 1 == 1.2345*2^100,但 1.2345*2^100 != 无穷大) 糟糕,在 int isinf( double x ) return x == x * 2; 之后 版,你现在有 isinf(0.0)... 如果x * 2 溢出怎么办?

以上是关于如何制作便携式 isnan/isinf 函数的主要内容,如果未能解决你的问题,请参考以下文章

NumPy: 通用函数

如何制作windows live writer绿色便携版

如何在WinPhone 8.1 Xamarin便携式项目中使用XFGloss

以便携方式检索传递给variadic函数的int32_t

便携式图书馆中的计时器

如何制作函数作曲家