Как использовать nan и inf в C? - PullRequest
83 голосов
/ 17 декабря 2009

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

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

Ответы [ 8 ]

77 голосов
/ 17 декабря 2009

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

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

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

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

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

a = NAN;

даже тогда,

a == NAN;

ложно. Один из способов проверить наличие NaN:

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

Вы также можете выполнить: a != a, чтобы проверить, является ли a значением NaN.

Существует также макросы 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).

Документы INFINITY Документы NAN

34 голосов
/ 17 декабря 2009

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

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

quiet_NaN() 
signalling_NaN()

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

infinity() 

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

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

20 голосов
/ 17 декабря 2009

Это работает как для float, так и для double:

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

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

19 голосов
/ 17 декабря 2009

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

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

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

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

ОБНОВЛЕНИЕ: протестировано и обновлено.

12 голосов
/ 01 августа 2014
double a_nan = strtod("NaN", NULL);
double a_inf = strtod("Inf", NULL);
3 голосов
/ 04 марта 2013
<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 голосов
/ 16 июля 2014

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

#define INFINITY (1e999)

или

const double INFINITY = 1e999

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

0 голосов
/ 17 декабря 2009

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

...