C++ 如何制作可移植的 isnan/isinf 函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2249110/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 22:42:03  来源:igfitidea点击:

how do I make a portable isnan/isinf function

c++cmathfunction

提问by monkeyking

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

我一直在 Linux 平台上使用isinf,isnan功能,效果很好。但这在 OS-X 上不起作用,所以我决定使用std::isinfstd::isnan哪个在 Linux 和 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

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

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

Does anyone know how this could be done?

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

edit:

编辑:

I ended up doing this in my source code for making isinf/isnanworking

我最终在我的制作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;
}

回答by math

You could also use boost for this task:

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

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

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

回答by Johann Hibschman

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.

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

回答by Amro

According to this, infinityis easy to check:

根据无穷远很容易检查:

  • sign = either 0 or 1 bit indicating positive/negative infinity.
  • exponent = all 1 bits.
  • mantissa = all 0 bits.
  • 符号 = 0 或 1 位,表示正/负无穷大。
  • 指数 = 全 1 位。
  • 尾数 = 全 0 位。

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

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

  • sign = either 0 or 1.
  • exponent = all 1 bits.
  • mantissa = anything except all 0 bits (since all 0 bits represents infinity).
  • 符号 = 0 或 1。
  • 指数 = 全 1 位。
  • 尾数 = 除所有 0 位之外的任何内容(因为所有 0 位都代表无穷大)。

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

下面是双精度浮点情况的代码。单精度可以类似地写(回想一下,双精度指数是 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;
}

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:

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

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

回答by Contango

This works under Visual Studio 2008:

这适用于 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.

为了安全起见,我建议使用 fpu_error()。我相信有些数字是使用 isnan() 获得的,有些数字是使用 isinf() 获得的,您需要两者都安全。

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.

回答by bobobobo

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

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

Now MSVC++ has an _isnanfunction in <float.h>.

现在MSVC++_isnan<float.h>.

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

适当的#defines 和#includes 应该做出合适的解决方法。

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

但是,我建议防止 nan 发生,而不是 nan detection

回答by paxdiablo

Well, ideally, you'd wait until Intel fixes the bug or provides a workaround :-)

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

But if you want to detect NaNand Inffrom 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.

但是,如果要检测NaN,并Inf从IEEE754值,其映射到一个整数(32位或64位取决于它的单或双精度),并检查指数位都是1。这表明这两个案件。

You can distinguish between NaNand Infby checking the high order bit of the mantissa. If it's 1, that's NaNotherwise Inf.

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

+/-Infis dictated by the sign bit.

+/-Inf由符号位决定。

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

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

Wikipediahas 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 :-)

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

As an aside, I've always found Harald Schmidt's IEEE754 convertervery useful for floating point analysis.

顺便说一句,我一直发现Harald Schmidt 的 IEEE754 转换器对于浮点分析非常有用。

回答by Dexter

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

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

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

This should be used:

这应该使用:

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

回答by rmacheshire

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

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

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

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

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

这适用于visual studio,但我不知道OS-X。

回答by Patapom

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

只需使用那个超级简单的符合 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; }

回答by Rupert

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

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