У меня есть числовой метод, который может вернуть nan или inf, если произошла ошибка, и для целей тестирования я хотел бы временно заставить его возвращать nan или inf, чтобы убедиться, что ситуация обрабатывается правильно. Есть ли надежный, независимый от компилятора способ создания значений nan и inf в C?
После 10 минут поиска в Google я смог найти только решения, зависящие от компилятора.
Ответы:
Вы можете проверить, есть ли это в вашей реализации:
#include <math.h> #ifdef NAN /* NAN is supported */ #endif #ifdef INFINITY /* INFINITY is supported */ #endif
Существование
INFINITY
гарантируется C99 (или, по крайней мере, последним черновиком) и «расширяется до постоянного выражения типа float, представляющего положительную или беззнаковую бесконечность, если доступно; в противном случае до положительной константы типа float, которая переполняется во время перевода».NAN
может быть определен или не определен, и «определяется тогда и только тогда, когда реализация поддерживает тихие NaN для типа float. Оно расширяется до константного выражения типа float, представляющего тихое 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 Docs NAN
источник
a != a
не должны НИКОГДА быть использованы.a
это не-число, дляa == NAN
возврата false. IEEE требует этого. Даже реализации, которые придерживаются IEEE, в основном это делают . Если этоisnan()
не реализовано, все же лучше обернуть тест, чем напрямую писать кодa == NAN
.Не существует независимого от компилятора способа сделать это, поскольку ни стандарты C (ни C ++) не говорят, что математические типы с плавающей запятой должны поддерживать NAN или INF.
Изменить: я только что проверил формулировку стандарта C ++, и в нем говорится, что эти функции (члены шаблонного класса numeric_limits):
вернет представления NAN «если доступно». Он не расширяет то, что означает «если доступно», но, по-видимому, что-то вроде «если представитель реализации FP поддерживает их». Аналогично есть функция:
который возвращает положительное сообщение INF "если доступно".
Оба они определены в
<limits>
заголовке - я предполагаю, что в стандарте C есть что-то подобное (возможно, также «если доступно»), но у меня нет копии текущего стандарта C99.источник
<math.h>
Определяетnan()
,nanf()
иnanl()
которые возвращают различные представления NaN (какdouble
,float
, и ,int
соответственно), и бесконечности (если имеющеся) могут быть возвращены путем генерации один сlog(0)
или что - то. Нет стандартного способа их проверить, даже в C99.<float.h>
Заголовок (<limits.h>
для целочисленных типов), к сожалению , умалчивает оinf
иnan
ценности.nanl()
возвращает along double
, а неint
как в моем комментарии. Не знаю, почему я этого не осознавал, когда печатал.Это работает как
float
иdouble
:double NAN = 0.0/0.0; double POS_INF = 1.0 /0.0; double NEG_INF = -1.0/0.0;
Изменить: как кто-то уже сказал, старый стандарт IEEE сказал, что такие значения должны вызывать ловушки. Но новые компиляторы почти всегда отключают ловушки и возвращают заданные значения, потому что перехват мешает обработке ошибок.
источник
#define is_nan(x) ((x) != (x))
может быть полезен как простой переносимый тест для NAN.Независимый от компилятора способ, но не независимый от процессора способ получить это:
int inf = 0x7F800000; return *(float*)&inf; int nan = 0x7F800001; return *(float*)&nan;
Это должно работать на любом процессоре, который использует формат с плавающей запятой IEEE 754 (что и делает x86).
ОБНОВЛЕНИЕ: Протестировано и обновлено.
источник
(float &)
? Мне это не похоже на C. Вам нужноint i = 0x7F800000; return *(float *)&i;
0x7f800001
это так называемый сигнальный NaN в стандарте IEEE-754. Хотя большинство библиотек и оборудования не поддерживают сигнальные NaN, вероятно, лучше вернуть тихий NaN0x7fc00000
.double a_nan = strtod("NaN", NULL); double a_inf = strtod("Inf", NULL);
источник
strtod
и преобразует NaN и Inf.<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-битные («длинные двойные») числа с плавающей запятой.источник
1e999
литералы больше не округляются до+Infinity
. По законам Мерфи это нарушает алгоритм. Хуже того: человек-программист, выполняющий «128-битную» сборку, вряд ли заметит эту ошибку заранее. Т.е. скорее всего будет поздно, когда эта ошибка будет обнаружена и распознана. Очень опасно.Вот простой способ определения этих констант, и я уверен, что он переносимый:
const double inf = 1.0/0.0; const double nan = 0.0/0.0;
Когда я запускаю этот код:
printf("inf = %f\n", inf); printf("-inf = %f\n", -inf); printf("nan = %f\n", nan); printf("-nan = %f\n", -nan);
Я получил:
источник