как сделать переносную функцию isnan / isinf - PullRequest
37 голосов
/ 12 февраля 2010

Я использую функции isinf, isnan на платформах Linux, которые отлично работали. Но это не сработало в OS-X, поэтому я решил использовать std::isinf std::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 ]

25 голосов
/ 10 сентября 2010

Вы также можете использовать повышение для этой задачи:

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

if( boost::math::isnan( ... ) .... )
20 голосов
/ 12 февраля 2010

Я не пробовал это, но я бы подумал

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

будет работать. Такое ощущение, что должен быть лучший способ для isinf, но это должно сработать.

15 голосов
/ 22 декабря 2013

Согласно это , бесконечность легко проверить:

  • знак = 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
7 голосов
/ 15 июня 2013

isnan теперь является частью C ++ 11, входит в GCC ++, как я полагаю, и в Apple LLVM.

Теперь MSVC ++ имеет функцию _isnan в <float.h>.

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

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

7 голосов
/ 18 июля 2010

Это работает в 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.
6 голосов
/ 12 февраля 2010

Ну, в идеале, вы должны подождать, пока 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 очень полезным для анализа с плавающей запятой.

2 голосов
/ 06 апреля 2011

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

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

Это следует использовать:

if ((boost::math::isnan)(number))
1 голос
/ 22 ноября 2016

Просто используйте этот супер простой 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; }
1 голос
/ 21 июня 2016

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

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

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

0 голосов
/ 22 сентября 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...