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

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

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

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;

ложно. Один из способов проверить, есть Ли Нэн:--16-->

#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()

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

infinity() 

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

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


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

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

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

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

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


это работает для обоих 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 case) или фактический Inf значение, оно останется бесконечным, даже если вы используете 128-битные ("длинные двойные") поплавки.