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

Я использовал функции 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;
}

Ответ 1

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

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

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

Ответ 2

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

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

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

Ответ 3

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

  • sign = либо 0, либо 1 бит, указывающий положительную/отрицательную бесконечность.
  • exponent = все 1 бит.
  • mantissa = все 0 бит.

NaN немного сложнее, потому что он не имеет уникального представления:

  • sign = либо 0, либо 1.
  • exponent = все 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

Ответ 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.

Ответ 6

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

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

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

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

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

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

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

Ответ 7

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

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

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

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

Ответ 8

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

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

Это работает с визуальной студией, но я не знаю об OS-X.

Ответ 10

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

Ответ 11

это работает на osx

#include <math.h>

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

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

изменить:

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

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