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