Как использовать nan и inf в C?



У меня есть численный метод, который может вернуть nan или inf, если была ошибка, и для тестирования я хотел бы временно заставить его вернуть nan или inf, чтобы убедиться, что ситуация обрабатывается правильно. Есть ли надежный,компилятор-независимая способ создания значений nan и inf в C?



после поиска в Google в течение примерно 10 минут я смог найти только зависимые от компилятора решения.

684   8  

8 ответов:

вы можете проверить, если ваша реализация это:

#include <math.h>
#ifdef NAN
/* NAN is supported */
#endif
#ifdef INFINITY
/* INFINITY is supported */
#endif

существование INFINITY гарантируется C99( или, по крайней мере, последним черновиком) и " расширяется до постоянного выражения типа float, представляющего положительное или беззнаковое бесконечность, если она доступна; иначе к положительной константе типа float, которая переполняется во время перевода."

NAN может или не может быть определено, и " определяется тогда и только тогда, когда реализация поддерживает тихие NaNs для типа float. Он расширяется до постоянного выражения типа float, представляющего собой Тихий NaN."

обратите внимание, что если вы сравниваете значения с плавающей запятой, и сделать:

a = NAN;

даже тогда,

a == NAN;

ложно. Один из способов проверить НАН будет:

#include <math.h>
if (isnan(a)) { ... }

вы также можете сделать: a != a чтобы проверить, если a Нэн.

есть еще isfinite(),isinf(),isnormal() и signbit() макросы в math.h в C99.

C99 также имеет nan функции:

#include <math.h>
double nan(const char *tagp);
float nanf(const char *tagp);
long double nanl(const char *tagp);

(ссылка: n1256).

Docs INFINITY Docs NAN

нет независимого от компилятора способа сделать это, поскольку ни стандарты C (ни c++) не говорят, что математические типы с плавающей запятой должны поддерживать NAN или INF.

Edit: Я только что проверил формулировку стандарта C++, и он говорит, что эти функции (члены шаблонного класса numeric_limits):

quiet_NaN() 
signalling_NaN()

будет осуществляться возврат НЭН представлений "при наличии". Он не распространяется на то, что означает "если доступно", но предположительно что-то вроде "если реализация FP rep поддерживает их". Аналогично существует функция:

infinity() 

который возвращает положительный INF rep "если доступно".

Они оба определены в <limits> заголовок-я бы предположил, что стандарт C имеет что-то подобное (вероятно, также "если доступно"), но у меня нет копии текущего стандарта C99.

независимый от компилятора способ, но не независимый от процессора способ получить эти:

int inf = 0x7F800000;
return *(float*)&inf;

int nan = 0x7F800001;
return *(float*)&nan;

Это должно работать на любом процессоре, который использует формат IEEE 754 с плавающей запятой (что x86 делает).

обновление: протестировано и обновлено.

это работает для обоих float и double:

double NAN = 0.0/0.0;
double POS_INF = 1.0 /0.0;
double NEG_INF = -1.0/0.0;

изменить: Как кто-то уже сказал, старый стандарт IEEE сказал, что такие значения должны вызывать ловушки. Но новые компиляторы почти всегда выключайте ловушки и возвращайте заданные значения, потому что захват препятствует ошибке обращение.

double a_nan = strtod("NaN", NULL);
double a_inf = strtod("Inf", NULL);
<inf.h>

/* IEEE positive infinity.  */

#if __GNUC_PREREQ(3,3)
# define INFINITY   (__builtin_inff())
#else
# define INFINITY   HUGE_VALF
#endif

и

<bits/nan.h>
#ifndef _MATH_H
# error "Never use <bits/nan.h> directly; include <math.h> instead."
#endif


/* IEEE Not A Number.  */

#if __GNUC_PREREQ(3,3)

# define NAN    (__builtin_nanf (""))

#elif defined __GNUC__

# define NAN \
  (__extension__                                  \
   ((union { unsigned __l __attribute__ ((__mode__ (__SI__))); float __d; })  \
    { __l: 0x7fc00000UL }).__d)

#else

# include <endian.h>

# if __BYTE_ORDER == __BIG_ENDIAN
#  define __nan_bytes       { 0x7f, 0xc0, 0, 0 }
# endif
# if __BYTE_ORDER == __LITTLE_ENDIAN
#  define __nan_bytes       { 0, 0, 0xc0, 0x7f }
# endif

static union { unsigned char __c[4]; float __d; } __nan_union
    __attribute_used__ = { __nan_bytes };
# define NAN    (__nan_union.__d)

#endif  /* GCC.  */

Я также удивлен, что это не константы времени компиляции. Но я полагаю, что вы могли бы создать эти значения достаточно легко, просто выполнив инструкцию, которая возвращает такой недопустимый результат. Деление на 0, лог 0, Тан 90, что-то вроде этого.

Я обычно использую

#define INFINITY (1e999)

или

const double INFINITY = 1e999

который работает по крайней мере в контекстах IEEE 754, потому что самое высокое представимое двойное значение примерно 1e308. 1e309 будет работать так же хорошо, как бы 1e99999, но три девятки достаточно и запоминающимся. Так как это либо двойной литерал (в #define случае) или Inf значение, оно останется бесконечным, даже если вы используете 128-битные ("длинные двойные") поплавки.

Comments

    Ничего не найдено.