Подтвердить что ты не робот

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

a = NAN;

даже тогда,

a == NAN;

является ложным. Один из способов проверить NaN:

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

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

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

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

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

(Ссылка: n1256).

Ответ 2

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

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

quiet_NaN() 
signalling_NaN()

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

infinity() 

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

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

Ответ 3

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

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

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

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

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

Ответ 4

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

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

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

Ответ 5

double a_nan = strtod("NaN", NULL);
double a_inf = strtod("Inf", NULL);

Ответ 6

<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.  */

Ответ 7

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

Ответ 8

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

#define INFINITY (1e999)

или

const double INFINITY = 1e999

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