Mam metodę numeryczną, która mogłaby zwrócić nan lub inf, jeśli wystąpił błąd, i dla celów testowania chciałbym tymczasowo zmusić ją do zwrócenia nan lub inf, aby upewnić się, że sytuacja jest obsługiwana poprawnie. Czy istnieje niezawodny, niezależny od kompilatora sposób tworzenia wartości nan i inf w języku C?
Po około 10 minutach wyszukiwania w Google udało mi się znaleźć tylko rozwiązania zależne od kompilatora.
Odpowiedzi:
Możesz sprawdzić, czy Twoja implementacja to ma:
#include <math.h> #ifdef NAN /* NAN is supported */ #endif #ifdef INFINITY /* INFINITY is supported */ #endif
Istnienie
INFINITY
jest gwarantowane przez C99 (lub przynajmniej najnowszą wersję roboczą) i „rozwija się do stałego wyrażenia typu float reprezentującego dodatnią lub bez znaku nieskończoność, jeśli jest dostępna; w przeciwnym razie do dodatniej stałej typu float, która przepełnia się w czasie tłumaczenia”.NAN
może być zdefiniowane lub nie, oraz „jest zdefiniowane wtedy i tylko wtedy, gdy implementacja obsługuje ciche NaN dla typu zmiennoprzecinkowego. Rozwija się do stałego wyrażenia typu float reprezentującego ciche NaN”.Zauważ, że jeśli porównujesz wartości zmiennoprzecinkowe, zrób:
nawet wtedy,
to fałsz. Jednym ze sposobów sprawdzenia NaN jest:
#include <math.h> if (isnan(a)) { ... }
Możesz także:
a != a
sprawdzić, czya
jest NaN.Jest też
isfinite()
,isinf()
,isnormal()
isignbit()
makr wmath.h
w C99.C99 ma również
nan
funkcje:#include <math.h> double nan(const char *tagp); float nanf(const char *tagp); long double nanl(const char *tagp);
(Odniesienie: n1256).
Dokumenty INFINITY Dokumenty NAN
źródło
a != a
powinna nigdy być używane.a
jest nie-liczbą,a == NAN
zwracało fałsz. IEEE tego wymaga. Nawet implementacje, które są zgodne z IEEE, robią to głównie . Gdyisnan()
nie jest zaimplementowany, nadal lepiej jest opakować test niż bezpośrednio kodowaća == NAN
.Nie ma na to niezależnego od kompilatora sposobu, ponieważ ani standardy C (ani C ++) nie mówią, że typy matematyczne zmiennoprzecinkowe muszą obsługiwać NAN lub INF.
Edycja: Właśnie sprawdziłem brzmienie standardu C ++ i mówi, że te funkcje (członkowie klasy numeric_limits):
zwróci reprezentacje NAN „jeśli są dostępne”. Nie rozwija tego, co oznacza „jeśli jest dostępny”, ale prawdopodobnie coś w stylu „jeśli przedstawiciel programu ramowego implementacji je obsługuje”. Podobnie jest funkcja:
co zwraca dodatni wynik INF „jeśli jest dostępny”.
Oba są zdefiniowane w
<limits>
nagłówku - domyślam się, że standard C ma coś podobnego (prawdopodobnie też "jeśli jest dostępny"), ale nie mam kopii obecnego standardu C99.źródło
<math.h>
Ustalanan()
,nanf()
inanl()
że obie różnymi reprezentacjami NaN (wdouble
,float
iint
, odpowiednio), a nieskończonością (jeśli jest dostępna) mogłaby być zwracana przez generowanie jednego zlog(0)
albo coś. Nie ma standardowego sposobu ich sprawdzenia, nawet w C99.<float.h>
Header (<limits.h>
dla integralnych typów) jest niestety milczy na ten tematinf
inan
wartości.nanl()
zwraca along double
, a nieint
jak mówi mój komentarz. Nie wiem, dlaczego nie zdawałem sobie z tego sprawy, kiedy to pisałem.Działa to zarówno w przypadku, jak
float
idouble
:double NAN = 0.0/0.0; double POS_INF = 1.0 /0.0; double NEG_INF = -1.0/0.0;
Edycja: Jak ktoś już powiedział, stary standard IEEE mówił, że takie wartości powinny powodować pułapki. Jednak nowe kompilatory prawie zawsze wyłączają pułapki i zwracają podane wartości, ponieważ pułapki przeszkadzają w obsłudze błędów.
źródło
#define is_nan(x) ((x) != (x))
może być przydatny jako prosty, przenośny test sieci NAN.Sposób niezależny od kompilatora, ale nie niezależny od procesora sposób na uzyskanie tych:
int inf = 0x7F800000; return *(float*)&inf; int nan = 0x7F800001; return *(float*)&nan;
Powinno to działać na każdym procesorze, który używa formatu zmiennoprzecinkowego IEEE 754 (co robi x86).
UPDATE: przetestowane i zaktualizowane.
źródło
(float &)
? To nie wygląda mi na C. Potrzebujeszint i = 0x7F800000; return *(float *)&i;
0x7f800001
jest to tak zwany sygnalizacyjny NaN w standardzie IEEE-754. Chociaż większość bibliotek i sprzętu nie obsługuje sygnalizacji NaN, prawdopodobnie lepiej jest zwrócić cichy typ NaN0x7fc00000
.double a_nan = strtod("NaN", NULL); double a_inf = strtod("Inf", NULL);
źródło
strtod
i konwertuje NaN i Inf.<inf.h> /* IEEE positive infinity. */ #if __GNUC_PREREQ(3,3) # define INFINITY (__builtin_inff()) #else # define INFINITY HUGE_VALF #endif
i
<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. */
źródło
Jestem również zaskoczony, że to nie są stałe czasu kompilacji. Ale przypuszczam, że możesz łatwo utworzyć te wartości, wykonując po prostu instrukcję, która zwraca taki nieprawidłowy wynik. Dzielenie przez 0, log z 0, tan z 90, coś w tym rodzaju.
źródło
Zwykle używam
#define INFINITY (1e999)
lub
const double INFINITY = 1e999
który działa przynajmniej w kontekstach IEEE 754, ponieważ najwyższa reprezentowalna wartość podwójna jest z grubsza
1e308
.1e309
działałyby równie dobrze, jak byłyby1e99999
, ale trzy dziewiątki są wystarczające i niezapomniane. Ponieważ jest to podwójny literał (w tym#define
przypadku) lub rzeczywistaInf
wartość, pozostanie nieskończona, nawet jeśli używasz 128-bitowych („długie podwójne”) wartości zmiennoprzecinkowe.źródło
1e999
literały nie są już zaokrąglane do+Infinity
. Według praw Murphy'ego, to łamie algorytm. Gorzej: programista wykonujący „128-bitową” kompilację prawdopodobnie nie zauważy wcześniej tego błędu. Tzn. Najprawdopodobniej będzie za późno, gdy ten błąd zostanie znaleziony i rozpoznany. Bardzo niebezpieczne.Oto prosty sposób zdefiniowania tych stałych i jestem prawie pewien, że jest przenośny:
const double inf = 1.0/0.0; const double nan = 0.0/0.0;
Kiedy uruchamiam ten kod:
printf("inf = %f\n", inf); printf("-inf = %f\n", -inf); printf("nan = %f\n", nan); printf("-nan = %f\n", -nan);
Dostaję:
źródło