Pytanie Jak korzystać z nan i inf w C?


Mam metodę numeryczną, która może zwrócić nan lub inf, jeśli wystąpił błąd, a dla celów testowych chciałbym tymczasowo zmusić go do powrotu nan lub inf, aby zapewnić poprawną obsługę sytuacji. Czy istnieje wiarygodny, niezależny od kompilatora sposób tworzenia wartości nan i inf w C?

Po przejściu go przez około 10 minut, udało mi się znaleźć rozwiązania zależne od kompilatora.


76
2017-12-17 18:57


pochodzenie


pływaki nie są zdefiniowane przez standard C. Tak więc nie ma niezależnego od kompilatora sposobu robienia tego, co chcesz. - Johan Kotlinski


Odpowiedzi:


Możesz sprawdzić, czy ma to twoja implementacja:

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

Istnienie INFINITY jest gwarantowane przez C99 (lub przynajmniej najnowszy szkic) i "rozwija się do stałego wyrażenia typu float reprezentującego pozytywny lub unsigned nieskończoność, jeśli jest dostępna; w przeciwnym razie do stałej dodatniej typu float, który przepełnia się w czasie tłumaczenia. "

NAN może być zdefiniowany lub nie, a "jest zdefiniowany wtedy i tylko wtedy, gdy implementacja obsługuje ciche NaN dla typu zmiennopozycyjnego i rozwija się do stałego wyrażenia typu float reprezentującego cichy NaN."

Zwróć uwagę, że jeśli porównujesz wartości zmiennoprzecinkowe i wykonaj:

a = NAN;

nawet wtedy,

a == NAN;

to fałsz. Jednym ze sposobów sprawdzenia NaN będzie:

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

Możesz także: a != a sprawdzić, czy a jest NaN.

Jest również isfinite(), isinf(), isnormal(), i signbit() makra w math.h w C99.

C99 również ma nan Funkcje:

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

(Numer referencyjny: n1256).


71
2017-12-17 19:15



Doskonała odpowiedź. Odniesienie do makr NAN i INFINITY to C99 §7.12 paragrafy 4 i 5. Poza tym (isnan (a)) możesz również sprawdzić NaN używając (a! = A) na zgodnej implementacji C. - Stephen Canon
Dzięki Stephen, zaktualizowany. - Alok Singhal
Z miłości do czytelności, a != a powinien NIGDY być użytym. - Chris Kerekes
@ChrisKerekes: Niestety, niektórzy z nas mają NAN, ale nie isnan (). Tak, to jest 2017. :( - eff


Nie ma niezależnego sposobu kompilacji, ponieważ ani C (ani C ++) nie mówią, że typy matematyczne zmiennoprzecinkowe muszą obsługiwać NAN lub INF.

Edytować: Właśnie sprawdziłem sformułowanie standardu C ++ i mówi on, że te funkcje (członkowie klasy numerycznej w szablonach):

quiet_NaN() 
signalling_NaN()

wiill zwraca reprezentacje NAN "jeśli są dostępne". Nie rozszerza się o to, co oznacza "jeśli jest dostępny", ale przypuszczalnie jest to coś w stylu "jeśli FP je wspiera". Podobnie jest funkcja:

infinity() 

która zwraca pozytywny powtórzenie INF "jeśli jest dostępny".

Oba są zdefiniowane w <limits> nagłówek - przypuszczam, że standard C ma coś podobnego (prawdopodobnie także "jeśli jest dostępny"), ale nie mam kopii aktualnego standardu C99.


33
2017-12-17 19:00



To rozczarowujące i zaskakujące. Czy C i C ++ nie odpowiadają liczbom zmiennoprzecinkowym IEEE, które mają standardową reprezentację dla nan i inf? - Graphics Noob
C nie narzuca reprezentacji zmiennoprzecinkowych IEEE. - Steve Emmerson
W C99, nagłówek C. <math.h> definiuje nan(), nanf(), i nanl() które zwracają różne reprezentacje NaN (jako double, float, i int odpowiednio) i nieskończoność (jeśli dostępna) może zostać zwrócona poprzez wygenerowanie jednego z nich log(0) lub coś. Nie ma standardowego sposobu na sprawdzenie ich, nawet w C99. The <float.h> nagłówek (<limits.h> jest dla typów integralnych) jest niestety cichy inf i nan wartości. - Chris Lutz
Wow, to wielka mieszanka. nanl() zwraca a long doublenie, nie int jak mój komentarz mówi. Nie wiem, dlaczego nie zdawałem sobie z tego sprawy, kiedy to pisałem. - Chris Lutz
@IngeHenriksen - Jestem pewien, że Microsoft stwierdził, że nie ma zamiaru VC ++ wspierającego C99. - Chris Lutz


Niezależny od kompilatora sposób, ale nie sposób niezależny od procesora:

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 (który x86 robi).

AKTUALIZACJA: przetestowany i zaktualizowany.


19
2017-12-17 19:08



@ WaffleMatt - dlaczego ten port nie byłby 32/64 bitowy? Pętla pojedynczej precyzji IEEE 754 jest 32-bitowa, niezależnie od wielkości adresowania procesora leżącego u jego podstaw. - Aaron
Przesyłanie do (float &) ? To nie wygląda na C dla mnie. Potrzebujesz int i = 0x7F800000; return *(float *)&i; - Chris Lutz
Zauważ, że 0x7f800001 jest tzw sygnalizacja NaN w standardzie IEEE-754. Chociaż większość bibliotek i sprzętu nie obsługuje sygnalizacji NaN, prawdopodobnie lepiej jest przywrócić ciche NaN 0x7fc00000. - Stephen Canon
Ostrzeżenie: to może wywołać Niezdefiniowane zachowanie przez naruszenie ścisłe aliasing zasady. Zalecany sposób (i najlepiej obsługiwany w kompilatorach) wpisz punning jest przez członkowie związku. - ulidtko
Może użyć int32_t zamiast int, jeśli to możliwe, dla przyszłej kompatybilności? - Keith M


Działa to dla obu float i double:

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

Edytować: Jak już ktoś powiedział, stary standard IEEE to potwierdził takie wartości powinny powodować pułapki. Ale nowe kompilatory prawie zawsze wyłączaj pułapki i zwracaj podane wartości, ponieważ pułapkowanie koliduje z błędem obsługa.


18
2017-12-17 19:26



Trapping był jedna opcja do obsługi błędów dozwolonych pod 754-1985. Zachowanie używane przez większość współczesnych urządzeń / kompilatorów było również dozwolone (i było preferowanym zachowaniem wielu członków komisji). Wielu realizatorów błędnie założyło, że pułapkowanie było wymagane ze względu na niefortunne użycie terminu "wyjątki" w standardzie. Zostało to znacznie wyjaśnione w poprawionym 754-2008. - Stephen Canon
Cześć, Stephen, masz rację, ale standard mówi również: "Użytkownik powinien mieć możliwość zażądania pułapki na każdym z pięciu wyjątków, określając dla niego funkcję obsługi. Powinien mieć możliwość zażądania wyłączenia istniejącego programu obsługi , powinien być zapisany lub odtworzony, powinien także być w stanie określić, czy określony program obsługi pułapek dla określonego wyjątku został włączony. " "powinien" zgodnie z definicją (2. Definicje) oznacza "zdecydowanie zalecane", a jego wdrożenie powinno zostać pominięte, jeśli architektura itp. czyni to niepraktycznym. 80x86 w pełni obsługuje standard, więc nie ma powodu, aby C go nie obsługiwał. - Thorsten S.
Zgadzam się, że C powinno wymagać zmiennoprzecinkowego 754 (2008), ale istnieją ku temu powody, aby tego nie robić; w szczególności C jest używane we wszystkich środowiskach innych niż x86 - w tym urządzenia osadzone, które nie mają sprzętu zmiennoprzecinkowego i urządzenia przetwarzające sygnały, w których programiści nie chcą nawet używać zmiennoprzecinkowego. Słusznie lub niesłusznie te zastosowania powodują dużą inercję w specyfikacji językowej. - Stephen Canon
Nie wiem, dlaczego tam powstała najwyższa odpowiedź. Nie daje żadnego sposobu na uzyskanie żądanych wartości. Ta odpowiedź brzmi. - drysdam


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

12
2017-08-01 13:07



To inteligentne przenośne rozwiązanie! Wymagane C99 strtod i konwertuje NaN i Inf. - ulidtko
Nie jest to wadą tego rozwiązania; nie są one stałymi. Nie można użyć tych wartości do zainicjowania zmiennej globalnej na przykład (lub do zainicjowania tablicy). - Marc
Nie działa dla mnie: VS 2008 po prostu wraca 0.0. - Keith M


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

3
2018-03-04 00:14





Jestem również zaskoczony, że nie są to stałe czasu kompilacji. Ale przypuszczam, że możesz łatwo utworzyć te wartości, po prostu wykonując instrukcję, która zwraca taki nieprawidłowy wynik. Dzieląc przez 0, log 0, tan 90, to coś.


0
2017-12-17 19:09





Zwykle używam

#define INFINITY (1e999)

lub

const double INFINITY = 1e999

który działa co najmniej w kontekstach IEEE 754, ponieważ najwyższa reprezentowalna wartość podwójna jest z grubsza 1e308. 1e309 działałby równie dobrze, jak by 1e99999, ale trzy dziewiątki są wystarczające i niezapomniane. Ponieważ jest to albo podwójne dosłowne (w #define case) lub rzeczywisty Inf wartość, pozostanie nieskończona, nawet jeśli używasz 128-bitowych ("długich podwójnych") spławików.


0
2017-07-15 22:58



Jest to bardzo niebezpieczne, moim zdaniem. Wyobraź sobie, jak ktoś przenosi twój kod na 128-bitowe zmienne wartości w ciągu około 20 lat (po tym jak twój kod przechodzi niewiarygodnie złożoną ewolucję, której żaden z etapów nie był w stanie przewidzieć dzisiaj). Nagle zasięg eksponatów drastycznie wzrasta, a cały twój 1e999 literały nie są już zaokrąglane do +Infinity. Zgodnie z prawem Murphy'ego, łamie to algorytm. Gorzej: ludzki programista robiący "128-bitową" kompilację prawdopodobnie nie zauważy tego błędu z góry. To znaczy. najprawdopodobniej będzie za późno kiedy ten błąd zostanie znaleziony i rozpoznany. Bardzo niebezpieczne. - ulidtko
Oczywiście najgorszy scenariusz powyżej może być daleka od realistycznej. Ale nadal rozważ alternatywę! Lepiej pozostać po bezpiecznej stronie. - ulidtko
"W ciągu 20 lat", heh. Daj spokój. Ta odpowiedź nie jest że zły. - alecov
@ulidtko Nie podoba mi się to, ale tak naprawdę? - Iharob Al Asimi
MSVC (16.0) generuje błąd "stała za duża". - Andreas H.