Как сделать переносную функцию isnan/isinf

Я использую isinf, isnan работает на платформах Linux, которые работали отлично. Но это не сработало на OS-X, поэтому я решил использовать std::isinfstd::isnan который работает на Linux и OS-X.

Но компилятор Intel не распознает его, и я думаю, что это ошибка компилятора Intel в соответствии с http://software.intel.com/en-us/forums/showthread.php?t=64188

Так что теперь я просто хочу избежать хлопот и определить свой собственный isinf, isnan реализация.

Кто-нибудь знает, как это можно сделать?

редактировать:

Я сделал это в моем исходном коде для создания 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;
}

11 ответов

Вы также можете использовать 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 немного сложнее, потому что не имеет уникального представления:

  • знак = 0 или 1.
  • экспонента = все 1 бит.
  • mantissa = все, кроме всех 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(), и вам нужно, чтобы оба были в безопасности.

Вот некоторый тестовый код:

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++ имеет _isnan функция в <float.h> ,

Подходящее #define с и #include ы должны сделать подходящий обходной путь.

Тем не менее, я рекомендую не допустить появления nan, вместо обнаружения nan.

Ну, в идеале, вы должны подождать, пока Intel исправит ошибку или предоставит обходной путь:-)

Но если вы хотите обнаружить NaN а также Inf из значений IEEE754 отобразите его в целое число (32 или 64 бита в зависимости от того, имеет ли он одинарную или двойную точность) и проверьте, равны ли все биты экспоненты 1. Это указывает на эти два случая.

Вы можете различить NaN а также Inf проверяя бит старшего разряда мантиссы. Если это 1, это NaN иначе Inf,

+/-Inf продиктовано знаком бит.

Для одинарной точности (32-битные значения) знак является старшим битом (b31), экспонента - следующие восемь бит (плюс 23-битная мантисса). Для двойной точности знак по-прежнему является старшим битом, но показатель степени составляет одиннадцать бит (плюс 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)

Просто помните, что этот код (но не метод) во многом зависит от размеров ваших длинных, которые не слишком переносимы. Но, если вам нужно немного покопаться, чтобы получить информацию, вы уже въехали на эту территорию:-)

Кроме того, я всегда находил преобразователь IEEE754 Харальда Шмидта очень полезным для анализа с плавающей запятой.

Просто используйте этот супер простой 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; }

Как сказал brubelsabs, Boost предлагает эту функцию, но, как сообщается здесь, вместо использования

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

Это должно быть использовано:

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

Кажется, никто не упомянул функцию C99 fpclassify, которая возвращает:

Один из типов FP_INFINITE, FP_NAN, FP_NORMAL, FP_SUBNORMAL, FP_ZERO или типа, определенного реализацией, указывающий категорию аргумента.

Это работает с visual studio, но я не знаю насчет OS-X.

В следующей статье есть несколько интересных трюков для isnan и isinf: http://jacksondunstan.com/articles/983

Это работает на OSX

#include <math.h>

также это может быть портативным,

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

редактировать:

как указывал Крис, вышеописанное может потерпеть неудачу с большим х

int isinf( double x ) { return x == x * 2; }
Другие вопросы по тегам