Sprawdzanie, czy double (lub float) jest NaN w C ++

368

Czy istnieje funkcja isnan ()?

PS .: Jestem w MinGW (jeśli to robi różnicę).

Miałem to rozwiązane za pomocą isnan () from <math.h>, który nie istnieje, w <cmath>którym byłem #includepoczątkowo.

hasen
źródło
2
Nie jestem pewien, że możesz to zrobić przenośnie. Kto powiedział, że C ++ wymaga IEEE754?
David Heffernan
Tylko uwaga, 1 uncja zapobiegania jest lepsza niż 1 funt lekarstwa. Innymi słowy, zapobieganie wykonywaniu 0.f / 0.f jest o wiele lepsze niż sprawdzanie wstecz nanw kodzie. nanmoże być strasznie destrukcyjne dla twojego programu, jeśli pozwolisz się rozprzestrzeniać, może wprowadzić trudne do znalezienia błędy. Wynika to z tego, że nanjest toksyczny (5 * nan= nan), nannie jest niczym ( nan! = nan), nanNie większy niż nic ( nan!> 0), nanjest nie mniejszy niż cokolwiek ( nan! <0).
bobobobo,
1
@bobobobo: Jest to funkcja umożliwiająca scentralizowane sprawdzanie błędów. Podobnie jak wyjątki vs. zwracane wartości.
Ben Voigt,
2
Dlaczego <cmath> nie ma isnan ()? Jest w std ::
frankliuao

Odpowiedzi:

349

Zgodnie ze standardem IEEE wartości NaN mają dziwną właściwość polegającą na tym, że porównania z nimi związane są zawsze fałszywe. To znaczy, dla liczby zmiennoprzecinkowej f f != fbędzie prawdą tylko wtedy, gdy f jest NaN.

Zauważ, że jak zauważyły ​​niektóre komentarze poniżej, nie wszystkie kompilatory przestrzegają tego podczas optymalizacji kodu.

Dla każdego kompilatora, który twierdzi, że używa zmiennoprzecinkowego IEEE, ta sztuczka powinna działać. Ale nie mogę zagwarantować, że to będzie działać w praktyce. W razie wątpliwości skontaktuj się ze swoim kompilatorem.

jalf
źródło
4
Kompilator nie powinien tego usuwać, jeśli działa w trybie IEEE. Sprawdź dokumentację swojego kompilatora, oczywiście ...
dmckee --- były kot moderator
38
-1 działa tylko w teorii, a nie w praktyce: kompilatory takie jak g ++ (z -fastmath) spieprzą to. jedynym ogólnym sposobem, do c ++ 0x, jest testowanie pod kątem bitpattern.
Pozdrawiam i hth. - Alf
66
@Alf: Dokumentacja -ffast-mathopcji wyraźnie mówi, że może to spowodować nieprawidłowe wyniki dla programów, które zależą od dokładnej implementacji, jeśli reguły / specyfikacje IEEE lub ISO dla funkcji matematycznych. Bez włączonej tej opcji używanie x != xjest całkowicie poprawnym i przenośnym sposobem testowania NaN.
Adam Rosenfield
7
@Adam: dokumentacja otwarcie stwierdza, że ​​jest niezgodna, tak. i tak, spotkałem się już z tym argumentem, omawiając to szczegółowo z Gabrielem Dos Reis. jest powszechnie używany do obrony projektu, w okrągłym sporze (nie wiem, czy chciałeś się z tym skojarzyć, ale warto wiedzieć o tym - to jest wojna o płomienie). Twój wniosek, który x != xjest ważny bez tej opcji, nie jest logiczny. może to być prawda dla konkretnej wersji g ++ lub nie. tak czy inaczej, generalnie nie ma możliwości zagwarantowania, że ​​opcja fastmath nie zostanie użyta.
Pozdrawiam i hth. - Alf
7
@Alf: Nie, nie byłem świadomy twojej dyskusji z Gabrielem Dos Reis. Steve Jessop doskonale podkreślił w innym pytaniu dotyczącym założenia reprezentacji IEEE. Jeśli założysz, że IEEE 754 i że kompilator działa w sposób zgodny (tj. Bez -ffast-mathopcji), to x != xjest to prawidłowe i przenośne rozwiązanie. Możesz nawet przetestować -ffast-math, testując __FAST_MATH__makro i w tym przypadku przełączyć się na inną implementację (np. Użyj unii i bitów kręconych).
Adam Rosenfield
220

W isnan()obecnej bibliotece standardowej C ++ nie jest dostępna żadna funkcja. Został wprowadzony w C99 i zdefiniowany jako makro a nie funkcja. Elementy standardowej biblioteki zdefiniowane przez C99 nie są częścią aktualnego standardu C ++ ISO / IEC 14882: 1998 ani jego aktualizacji ISO / IEC 14882: 2003.

W 2005 r. Zaproponowano sprawozdanie techniczne 1. TR1 zapewnia zgodność z C99 do C ++. Pomimo tego, że nigdy nie zostało oficjalnie przyjęte jako standard C ++, wiele ( implementacje GCC 4.0+ lub Visual C ++ 9.0+ C ++ zapewniają funkcje TR1, wszystkie lub tylko niektóre (Visual C ++ 9.0 nie zapewnia funkcji matematycznych C99) .

Jeśli TR1 jest dostępna, a następnie cmathzawiera elementy, takie jak C99 isnan(), isfinite()itp ale są one zdefiniowane jako funkcje, niewymienione makr, zazwyczaj w std::tr1::przestrzeni nazw, chociaż wiele implementacji (tj GCC 4+ w systemie Linux lub w Xcode na Mac OS X 10.5+) wstrzyknąć bezpośrednio do std::, więc std::isnanjest dobrze zdefiniowane.

Co więcej, niektóre implementacje C ++ wciąż udostępniają isnan()makro C99 dla C ++ (zawarte przez cmathlub math.h), co może powodować więcej zamieszania, a programiści mogą założyć, że jest to standardowe zachowanie.

Uwaga na temat Viusal C ++, jak wspomniano powyżej, nie zapewnia std::isnanżadnej z nich std::tr1::isnan, ale zapewnia funkcję rozszerzenia zdefiniowaną jako _isnan()dostępna od wersji Visual C ++ 6.0

Na XCode jest jeszcze więcej zabawy. Jak wspomniano, GCC 4+ definiuje std::isnan. Wydaje się, że w starszych wersjach kompilatora i biblioteki XCode (tutaj jest odpowiednia dyskusja ), nie miałem okazji się sprawdzić) zdefiniowano dwie funkcje, __inline_isnand()na Intel i __isnand()Power PC.

mloskot
źródło
21
Każdy chce mieć takie funkcje jak isNan lub isInfinity. Dlaczego osoby odpowiedzialne nie tylko uwzględniają w swoich standardach ???? - Spróbuję dowiedzieć się, jak się dowodzić, i oddam na to głos. Poważnie.
shuhalo,
8
@shuhalo Za jeszcze?
Tomáš Zato - Przywróć Monikę
11
Ta odpowiedź powinna zostać zaktualizowana, ponieważ std::isnanjest teraz częścią standardu C ++ 11, a wsparcie zostało rozszerzone. std :: isnan został zaimplementowany w Visual Studio od Visual Studio 2013. Być może @shuhalo dowodził :-)
aberaud
170

Pierwsze rozwiązanie: jeśli używasz C ++ 11

Ponieważ o to zapytano, pojawiło się trochę nowych rozwiązań: ważne jest, aby wiedzieć, że std::isnan()jest to część C ++ 11

Streszczenie

Zdefiniowane w nagłówku <cmath>

bool isnan( float arg ); (since C++11)
bool isnan( double arg ); (since C++11)
bool isnan( long double arg ); (since C++11)

Określa, czy podana liczba zmiennoprzecinkowa arg nie jest liczbą ( NaN).

Parametry

arg: wartość zmiennoprzecinkowa

Zwracana wartość

truejeśli arg jest NaN, w falseprzeciwnym razie

Odniesienie

http://en.cppreference.com/w/cpp/numeric/math/isnan

Pamiętaj, że jest to niezgodne z opcją -fast-matematyki, jeśli używasz g ++, zobacz inne sugestie poniżej.


Inne rozwiązania: jeśli używasz narzędzi niezgodnych z C ++ 11

W przypadku C99 w C jest to zaimplementowane jako makro, isnan(c)które zwraca wartość int. Typ xpowinien być zmiennoprzecinkowy, podwójny lub długi podwójny.

Różni dostawcy mogą, ale nie muszą zawierać funkcji isnan().

Niby przenośny sposób na sprawdzenie NaNto użycie właściwości IEEE 754, która NaNnie jest sobie równa: tj. x == xBędzie fałszywa za xbycie NaN.

Jednak ostatnia opcja może nie działać z każdym kompilatorem i niektórymi ustawieniami (szczególnie ustawieniami optymalizacji), więc w ostateczności zawsze możesz sprawdzić wzorzec bitowy ...

BlueTrin
źródło
8
Zdecydowanie zasługuje na zaakceptowaną odpowiedź i zasługuje na więcej pozytywnych opinii. Dzięki za wskazówkę
LBes
3
−1 std::isnan jest wciąż niezbyt dobrą rekomendacją na luty 2017, ponieważ nie działa z optymalizacją zmiennoprzecinkową g ++.
Pozdrawiam i hth. - Alf
@ Cheersandhth.-Alf: czy ta opcja jest zgodna z IEEE? Odpowiedź została zredagowana
BlueTrin
@BlueTrin: Oba x != xi isnansą wymagane do pracy na zgodność z IEEE 754. W odniesieniu do tego ostatniego standard IEEE 754-2008 stanowi, że „Implementacje powinny zapewniać następujące operacje obliczeniowe dla wszystkich obsługiwanych formatów arytmetycznych”, a „isNaN (x) jest prawdą wtedy i tylko wtedy, gdy x jest NaN”. Do sprawdzania zgodności wymagana jest ta norma is754version1985()oraz is754version2008()tam, gdzie oferuje to C ++ std::numeric_limits<Fp>::is_iec559()(ta sama norma to IEC 559). Niestety z -ffast-mathoptymalizacją, np. G ++ twierdzi zgodność, ale jest niezgodna.
Pozdrawiam i hth. - Alf
1
Ostrzeżenie: isnan (x) nie działa z opcją -ffinite-matematyki-tylko w gcc i clang
A Fog
82

W programie Boost dostępna jest także biblioteka tylko z nagłówkami, która zawiera zgrabne narzędzia do obsługi typów danych zmiennoprzecinkowych

#include <boost/math/special_functions/fpclassify.hpp>

Otrzymasz następujące funkcje:

template <class T> bool isfinite(T z);
template <class T> bool isinf(T t);
template <class T> bool isnan(T t);
template <class T> bool isnormal(T t);

Jeśli masz czas, zapoznaj się z całym zestawem narzędzi matematycznych z Boost, ma wiele przydatnych narzędzi i szybko rośnie.

Również w przypadku punktów zmiennoprzecinkowych i nieprzemiennych dobrym pomysłem może być przyjrzenie się konwersji liczbowej .

Anonimowy
źródło
1
Dzięki! Właśnie tego szukałem.
Dr Watson
został dodany w Boost 1.35 (właśnie odkryłem, że mój program nie kompiluje się na starej dystrybucji Linuksa).
marcin
2
jeśli skompilujesz z opcją --fast-matematyki, ta funkcja nie będzie działać zgodnie z oczekiwaniami.
Gaetano Mendola
43

Istnieją trzy „oficjalne” sposoby: isnanmakro posix , isnanszablon funkcji c ++ 0x lub wizualna _isnanfunkcja c ++ .

Niestety wykrycie, którego z nich użyć, jest raczej niepraktyczne.

I niestety nie ma niezawodnego sposobu na wykrycie, czy masz reprezentację IEEE 754 z NaN. Standardowa biblioteka oferuje taki oficjalny sposób ( numeric_limits<double>::is_iec559). Ale w praktyce kompilatory takie jak g ++ to psują.

Teoretycznie można użyć po prostu x != x, ale kompilatory takie jak g ++ i visual c ++ to psują.

W końcu przetestuj określone bity wzorcowe NaN , zakładając (i mam nadzieję, że w pewnym momencie wymuszę !) Określoną reprezentację, taką jak IEEE 754.


EDYCJA : jako przykład „kompilatorów takich jak g ++… spieprz to”, rozważ

#include <limits>
#include <assert.h>

void foo( double a, double b )
{
    assert( a != b );
}

int main()
{
    typedef std::numeric_limits<double> Info;
    double const nan1 = Info::quiet_NaN();
    double const nan2 = Info::quiet_NaN();
    foo( nan1, nan2 );
}

Kompilacja z g ++ (TDM-2 mingw32) 4.4.1:

C: \ test> wpisz „C: \ Program Files \ @commands \ gnuc.bat”
@rem -finput-charset = windows-1252
@ g ++ -O -pedantic -std = c ++ 98 -Wall -Wwrite-strings% * -Wno-long-long

C: \ test> gnuc x.cpp

C: \ test> echo && działa ... || echo! nie powiodło się
Pracuje...

C: \ test> gnuc x.cpp - fast-math

C: \ test> echo && działa ... || echo! nie powiodło się
Asercja nie powiodła się: a! = B, plik x.cpp, wiersz 6

Ta aplikacja poprosiła środowisko wykonawcze o zakończenie go w nietypowy sposób.
Skontaktuj się z zespołem pomocy technicznej aplikacji, aby uzyskać więcej informacji.
! nie powiodło się

C: \ test> _
Pozdrawiam i hth. - Alf
źródło
4
@Alf: Twój przykład działa zgodnie z oczekiwaniami dla mnie zarówno w systemie Mac OS X, jak i Linux w różnych wersjach g ++ między 4.0 a 4.5. Dokumentacja -ffast-mathopcji wyraźnie mówi, że może to spowodować nieprawidłowe wyniki dla programów, które zależą od dokładnej implementacji, jeśli reguły / specyfikacje IEEE lub ISO dla funkcji matematycznych. Bez włączonej tej opcji używanie x != xjest całkowicie poprawnym i przenośnym sposobem testowania NaN.
Adam Rosenfield
6
@Adam: Brakuje Ci tego, że standard C ++ nie wymaga reprezentacji IEEE ani matematyki dla liczb zmiennoprzecinkowych. O ile strona podręcznika mówi ci, gcc -ffast-mathwciąż jest zgodną implementacją C ++ (no cóż, zakładając, że działa numeric_limits::is_iec559poprawnie, jest, chociaż Alf sugeruje powyżej, że tak nie jest): Kod C ++ polegający na IEEE nie jest przenośnym C ++ i nie ma prawa oczekiwać implementacji, które to zapewnią.
Steve Jessop
5
I Alf ma rację, szybki test na gcc 4.3.4 i is_iec559jest to prawda -ffast-math. Problem polega na tym, że dokumenty GCC -ffast-mathmówią tylko, że nie jest to IEEE / ISO dla funkcji matematycznych, podczas gdy powinni powiedzieć, że to nie jest C ++, ponieważ jego implementacja numeric_limitsjest błędna. Domyślam się, że GCC nie zawsze może stwierdzić w momencie definiowania szablonu, czy ostateczny backend rzeczywiście ma zgodne zmiennoprzecinkowe, a więc nawet nie próbuje. IIRC istnieją podobne problemy na liście zaległych błędów zgodności G99 z C99.
Steve Jessop
1
@Alf, @Steve, nie wiedziałem, że standard C ++ nie ma specyfikacji na temat wartości zmiennoprzecinkowych. To dla mnie dość szokujące. Wygląda lepiej, obsługując IEEE 754 i NaN jako rozszerzenie specyficzne dla platformy zamiast standardowego. Czyż nie I czy mogę się spodziewać jakiegokolwiek rodzaju isnan () lub IEEE754 dodanego w C ++ 0x?
Eonil
3
@Eonil: C ++ 0x nadal ma na przykład „Reprezentacja wartości typów punktów pływających jest zdefiniowana w implementacji”. Zarówno C, jak i C ++ mają wspierać implementacje na maszynach bez sprzętu zmiennoprzecinkowego, a odpowiednie zmiennoprzecinkowe IEEE 754 mogą być nieco wolniejsze do emulacji niż rozsądnie dokładne alternatywy. Teorią jest to, że możesz twierdzić, is_iec559jeśli potrzebujesz IEEE, w praktyce nie wydaje się to działać na GCC. C ++ 0x ma jakąś isnanfunkcję, ale ponieważ GCC nie teraz poprawnie implementuje is_iec559, myślę, że nie będzie w C ++ 0x i -ffast-mathmoże ją zepsuć isnan.
Steve Jessop
39

Istnieje std :: isnan, jeśli kompilator obsługuje rozszerzenia c99, ale nie jestem pewien, czy mingw to robi.

Oto mała funkcja, która powinna działać, jeśli twój kompilator nie ma standardowej funkcji:

bool custom_isnan(double var)
{
    volatile double d = var;
    return d != d;
}
CTT
źródło
6
dlaczego nie tylko var! = var?
Brian R. Bondy
8
Gdy to robi, kompilator zoptymalizuje porównanie, zawsze zwracając wartość true.
CTT
23
Nie, nie ma. Kompilator, który to robi, jest uszkodzony. Równie dobrze możesz powiedzieć, że istnieje szansa, że ​​standardowa biblioteka isnanzwróci zły wynik. Technicznie prawdą jest, że kompilator może być wadliwy, ale w praktyce Not Gonna Happen. Tak samo jak var != var. Działa, ponieważ tak definiowane są wartości zmiennoprzecinkowe IEEE.
czerwiec
29
jeśli ustawiono -ffast-math, isnan () nie zwróci poprawnego wyniku dla gcc. Oczywiście, ta optymalizacja jest udokumentowana jako przełamująca semantykę IEEE ...
Matthew Herrmann
Jeśli ustawiona jest opcja -ffast-matematyka, kompilator jest uszkodzony. A raczej, jeśli ustawiona jest opcja -ffast-matematyka, wszystkie zakłady są wyłączone i i tak nie można polegać na NaN.
Adrian Ratnapala
25

Możesz użyć numeric_limits<float>::quiet_NaN( )zdefiniowanego w limitsstandardowej bibliotece do testowania. Zdefiniowano osobną stałą double.

#include <iostream>
#include <math.h>
#include <limits>

using namespace std;

int main( )
{
   cout << "The quiet NaN for type float is:  "
        << numeric_limits<float>::quiet_NaN( )
        << endl;

   float f_nan = numeric_limits<float>::quiet_NaN();

   if( isnan(f_nan) )
   {
       cout << "Float was Not a Number: " << f_nan << endl;
   }

   return 0;
}

Nie wiem, czy to działa na wszystkich platformach, ponieważ testowałem tylko z g ++ w systemie Linux.

Bill jaszczurka
źródło
2
Uważaj jednak - wydaje się, że w GCC wersja 3.2.3 występuje błąd w ograniczeniach numerycznych, ponieważ zwraca wartość 0,0 dla wartości cichej_naN. Późniejsze wersje GCC są w porządku z mojego doświadczenia.
Nathan Kitchen
@Nathan: Dobrze wiedzieć. Używam wersji 4.3.2, więc jestem poza lasem.
Bill the Lizard
18

Możesz użyć tej isnan()funkcji, ale musisz dołączyć bibliotekę matematyki C.

#include <cmath>

Ponieważ ta funkcja jest częścią C99, nie jest dostępna wszędzie. Jeśli twój dostawca nie dostarcza tej funkcji, możesz również zdefiniować własny wariant dla kompatybilności.

inline bool isnan(double x) {
    return x != x;
}
raimue
źródło
Używałem <cmath> i nie ma w tym isnan! przypadkowo dowiedziałem się, że tam jestisnan w <math.h>
hasen
1
Jak powiedziałem, jest to część C99. Ponieważ C99 nie jest częścią żadnego obecnego standardu C ++, podałem alternatywę. Ale ponieważ jest prawdopodobne, że isnan () zostanie włączony do nadchodzącego standardu C ++, umieściłem wokół niego dyrektywę #ifndef.
raimue
12

Poniższy kod wykorzystuje definicję NAN (zestaw wszystkich bitów wykładniczych, przynajmniej jeden zestaw bitów ułamkowych) i zakłada, że ​​sizeof (int) = sizeof (float) = 4. Szczegółowe informacje można znaleźć w Wikipedii w NAN.

bool IsNan( float value ) { return ((*(UINT*)&value) & 0x7fffffff) > 0x7f800000; }

Ian
źródło
Wierzę, że działałoby to również na dużych platformach Endian. Dosłowność 0x7fffffffpo prostu pozostanie w pamięci jako ff ff ff 7f. valuema taką samą kolejność jak ma 0x7f800000, więc wszystkie operacje są w linii (nie ma zamiany bajtów). Byłbym zainteresowany, gdyby ktoś mógł to przetestować na platformie Big Endian.
Bryan W. Wagner,
0x7fff1234jest również NaN. Tak też jest0xffffffff
Steve Hollasch,
12

zapobieganie nanom

Moja odpowiedź na to pytanie nienan polega na sprawdzaniu z mocą wsteczną . Zamiast tego użyj kontroli zapobiegawczej dla podziałów formularza 0.0/0.0.

#include <float.h>
float x=0.f ;             // I'm gonna divide by x!
if( !x )                  // Wait! Let me check if x is 0
  x = FLT_MIN ;           // oh, since x was 0, i'll just make it really small instead.
float y = 0.f / x ;       // whew, `nan` didn't appear.

nanwynika z operacji 0.f/0.flub 0.0/0.0. nanjest straszną przeszkodą dla stabilności twojego kodu, którą trzeba bardzo ostrożnie wykryć 1 . Ich właściwości nanróżnią się od normalnych liczb:

  • nanjest toksyczny, (5 * nan= nan)
  • nannie jest niczym, nawet sam ( nan! = nan)
  • nannie większy niż cokolwiek ( nan!> 0)
  • nanjest nie mniej niż cokolwiek ( nan! <0)

Dwie ostatnie wymienione właściwości są przeciwne logice i spowodują dziwne zachowanie kodu, który opiera się na porównaniach z nanliczbą (trzecia ostatnia właściwość jest również nieparzysta, ale prawdopodobnie nigdy nie zobaczyszx != x ? w swoim kodzie (chyba że sprawdzasz dla nan (niewiarygodnie))).

W swoim własnym kodzie zauważyłem, że nanwartości często powodują błędy, które można znaleźć. (Zauważ, że tak nie jest w przypadku influb -inf. ( -inf<0) zwraca TRUE, (0 < inf) zwraca PRAWDA, a nawet ( -inf< inf) zwraca PRAWDA. Tak więc, z mojego doświadczenia, zachowanie kodu często jest nadal pożądane).

co robić pod nan

To, co chcesz się wydarzyć, 0.0/0.0 musi być traktowane jako szczególny przypadek , ale to, co robisz, musi zależeć od liczb, których oczekujesz od kodu.

W powyższym przykładzie wynikiem ( 0.f/FLT_MIN) będzie w 0zasadzie. Zamiast tego możesz chcieć 0.0/0.0wygenerować HUGE. Więc,

float x=0.f, y=0.f, z;
if( !x && !y )    // 0.f/0.f case
  z = FLT_MAX ;   // biggest float possible
else
  z = y/x ;       // regular division.

Tak więc powyżej, gdyby x było 0.f,inf spowodowałoby (które ma całkiem dobre / nieniszczące zachowanie, jak wspomniano powyżej).

Pamiętaj, dzielenie liczb całkowitych przez 0 powoduje wyjątek czasu wykonywania . Dlatego zawsze musisz sprawdzać dzielenie liczb całkowitych przez 0. To, że 0.0/0.0cicho ocenia nan, nie oznacza, że ​​możesz być leniwy i nie sprawdzać, 0.0/0.0zanim to nastąpi.

1 Czeki przez nanvia x != xsą czasami niewiarygodne ( x != xsą usuwane przez niektóre optymalizujące kompilatory, które łamią zgodność z IEEE, szczególnie gdy -ffast-mathprzełącznik jest włączony).

Bobobobo
źródło
Dzięki za zwrócenie na to uwagi; takie programowanie zdecydowanie pomogłoby w rozwiązaniu problemu jako takiego. Ale następnym razem staraj się nie nadużywać funkcji formatowania tekstu. Zmiana rozmiarów czcionek, gramatury i stylu sprawia, że ​​czytanie jest bardzo trudne.
Magnus
4
Należy zauważyć, że 0,0 / 0,0 nie jest jedyną operacją, która może spowodować powstanie NaN. Pierwiastek kwadratowy liczby ujemnej zwraca NaN. Cosinus + nieskończoności zwraca również NaN. operacja acos (x), w której x nie jest w zakresie [0, pi], może również dawać NaN. W skrócie, należy szczególnie uważać, aby spojrzeć na te potencjalnie ryzykowne operacje, nie tylko na 0,0 / 0,0.
Boris Dalstein
Całkowicie zgadzam się z Borisem. Z mojego doświadczenia wynika, że ​​NaN praktycznie zawsze pochodzi z czegoś takiego jak sqrt (-1,302e-53), tj. Wyniki obliczeń pośrednich bliskie zera są wprowadzane do sqrt bez sprawdzania negatywności.
hans_meine
1
„Zapobieganie NaN” oznacza, że ​​musisz dostać się do wszystkich podstawowych operacji arytmetycznych, a nie tylko podziału. Musisz uważać na ∞ / ∞, 0 * ∞, ∞% x, x% 0, ∞ - ∞, 0 ^ 0, ∞ ^ 0, i wiele innych. Bycie „zapobiegawczym” przy tak podstawowych operacjach arytmetycznych oznacza, że ​​całkowicie zatankujesz swoją wydajność (i prawdopodobnie przegapisz dodatkowe przypadki, o których nie pomyślałeś).
Steve Hollasch,
11

Począwszy od C ++ 14 istnieje wiele sposobów sprawdzenia, czy liczba zmiennoprzecinkowa valuejest NaN.

Spośród tych sposobów tylko sprawdzanie bitów reprezentacji liczby działa niezawodnie, jak zauważono w mojej pierwotnej odpowiedzi. W szczególności std::isnani często proponowana kontrola v != vnie działa niezawodnie i nie powinna być używana, aby Twój kod nie przestał działać poprawnie, gdy ktoś zdecyduje, że potrzebna jest optymalizacja zmiennoprzecinkowa, i poprosi kompilator o zrobienie tego. Ta sytuacja może się zmienić, kompilatory mogą uzyskać większą zgodność, ale w przypadku tego problemu, który nie pojawił się przez 6 lat od pierwotnej odpowiedzi.

Przez około 6 lat moją pierwotną odpowiedzią było wybrane rozwiązanie tego pytania, które było OK. Ale ostatnio wybrano bardzo pozytywną odpowiedź zalecającą niewiarygodny v != vtest. Stąd ta dodatkowa, bardziej aktualna odpowiedź (teraz mamy standardy C ++ 11 i C ++ 14 oraz C ++ 17 na horyzoncie).


Główne sposoby sprawdzania obecności NaN od C ++ 14 to:

  • std::isnan(value) )
    jest zamierzonym standardowym sposobem biblioteki od C ++ 11. isnannajwyraźniej jest w konflikcie z makrem Posix o tej samej nazwie, ale w praktyce nie stanowi to problemu. Główny problem polega na tym, że gdy wymagana jest optymalizacja arytmetyczna zmiennoprzecinkowa, wówczas co najmniej jeden główny kompilator, mianowicie g ++, std::isnan zwraca falseargument NaN .

  • (fpclassify(value) == FP_NAN) )
    Cierpi na ten sam problem, co std::isnannp. Nie jest wiarygodny.

  • (value != value) )
    Zalecane w wielu odpowiedziach SO. Cierpi na ten sam problem, co std::isnannp. Nie jest wiarygodny.

  • (value == Fp_info::quiet_NaN()) )
    Jest to test, który przy standardowym zachowaniu nie powinien wykrywać NaN, ale że przy zoptymalizowanym zachowaniu może wykryć NaN (ze względu na zoptymalizowany kod bezpośrednio porównujący reprezentacje poziomu bitów) i być może w połączeniu z innym sposobem na pokrycie standardowego niezoptymalizowanego zachowania , mógł niezawodnie wykryć NaN. Niestety okazało się, że nie działa niezawodnie.

  • (ilogb(value) == FP_ILOGBNAN) )
    Cierpi na ten sam problem, co std::isnannp. Nie jest wiarygodny.

  • isunordered(1.2345, value) )
    Cierpi na ten sam problem, co std::isnannp. Nie jest wiarygodny.

  • is_ieee754_nan( value ) )
    To nie jest standardowa funkcja. Sprawdza bity zgodnie ze standardem IEEE 754. Jest całkowicie niezawodny, ale kod jest w pewnym stopniu zależny od systemu.


W poniższym kompletnym kodzie testowym „sukces” określa, czy wyrażenie zgłasza Nanowość wartości. Dla większości wyrażeń ta miara sukcesu, cel wykrycia NaN i tylko NaN, odpowiada ich standardowej semantyce. Jednak w przypadku (value == Fp_info::quiet_NaN()) )wyrażenia standardowe zachowanie polega na tym, że nie działa ono jako detektor NaN.

#include <cmath>        // std::isnan, std::fpclassify
#include <iostream>
#include <iomanip>      // std::setw
#include <limits>
#include <limits.h>     // CHAR_BIT
#include <sstream>
#include <stdint.h>     // uint64_t
using namespace std;

#define TEST( x, expr, expected ) \
    [&](){ \
        const auto value = x; \
        const bool result = expr; \
        ostringstream stream; \
        stream << boolalpha << #x " = " << x << ", (" #expr ") = " << result; \
        cout \
            << setw( 60 ) << stream.str() << "  " \
            << (result == expected? "Success" : "FAILED") \
            << endl; \
    }()

#define TEST_ALL_VARIABLES( expression ) \
    TEST( v, expression, true ); \
    TEST( u, expression, false ); \
    TEST( w, expression, false )

using Fp_info = numeric_limits<double>;

inline auto is_ieee754_nan( double const x )
    -> bool
{
    static constexpr bool   is_claimed_ieee754  = Fp_info::is_iec559;
    static constexpr int    n_bits_per_byte     = CHAR_BIT;
    using Byte = unsigned char;

    static_assert( is_claimed_ieee754, "!" );
    static_assert( n_bits_per_byte == 8, "!" );
    static_assert( sizeof( x ) == sizeof( uint64_t ), "!" );

    #ifdef _MSC_VER
        uint64_t const bits = reinterpret_cast<uint64_t const&>( x );
    #else
        Byte bytes[sizeof(x)];
        memcpy( bytes, &x, sizeof( x ) );
        uint64_t int_value;
        memcpy( &int_value, bytes, sizeof( x ) );
        uint64_t const& bits = int_value;
    #endif

    static constexpr uint64_t   sign_mask       = 0x8000000000000000;
    static constexpr uint64_t   exp_mask        = 0x7FF0000000000000;
    static constexpr uint64_t   mantissa_mask   = 0x000FFFFFFFFFFFFF;

    (void) sign_mask;
    return (bits & exp_mask) == exp_mask and (bits & mantissa_mask) != 0;
}

auto main()
    -> int
{
    double const v = Fp_info::quiet_NaN();
    double const u = 3.14;
    double const w = Fp_info::infinity();

    cout << boolalpha << left;
    cout << "Compiler claims IEEE 754 = " << Fp_info::is_iec559 << endl;
    cout << endl;;
    TEST_ALL_VARIABLES( std::isnan(value) );                    cout << endl;
    TEST_ALL_VARIABLES( (fpclassify(value) == FP_NAN) );        cout << endl;
    TEST_ALL_VARIABLES( (value != value) );                     cout << endl;
    TEST_ALL_VARIABLES( (value == Fp_info::quiet_NaN()) );      cout << endl;
    TEST_ALL_VARIABLES( (ilogb(value) == FP_ILOGBNAN) );        cout << endl;
    TEST_ALL_VARIABLES( isunordered(1.2345, value) );           cout << endl;
    TEST_ALL_VARIABLES( is_ieee754_nan( value ) );
}

Wyniki z g ++ (zauważ, że standardowe zachowanie (value == Fp_info::quiet_NaN())jest takie, że nie działa jako detektor NaN, jest to po prostu bardzo praktyczne zainteresowanie tutaj):

[C: \ my \ forums \ so \ 282 (wykryj NaN)]
> g ++ --wersja | znajdź „++”
g ++ (x86_64-win32-sjlj-rev1, zbudowany przez projekt MinGW-W64) 6.3.0

[C: \ my \ forums \ so \ 282 (wykryj NaN)]
> g ++ foo.cpp && a
Kompilator twierdzi, że IEEE 754 = true

v = nan, (std :: isnan (wartość)) = true Sukces
u = 3,14, (std :: isnan (wartość)) = false Sukces
w = inf, (std :: isnan (wartość)) = false Sukces

v = nan, ((fpclassify (wartość) == 0x0100)) = true Sukces
u = 3,14, ((fpclassify (wartość) == 0x0100)) = fałszywy sukces
w = inf, ((fpclassify (wartość) == 0x0100)) = false Sukces

v = nan, ((wartość! = wartość)) = true Sukces
u = 3,14, ((wartość! = wartość)) = false Sukces
w = inf, ((wartość! = wartość)) = false Sukces

v = nan, ((wartość == Fp_info :: quiet_NaN ())) = false FAILED
u = 3,14, ((wartość == Fp_info :: quiet_NaN ())) = false Sukces
w = inf, ((wartość == Fp_info :: quiet_NaN ())) = false Sukces

v = nan, ((ilogb (wartość) == ((int) 0x80000000))) = true Sukces
u = 3,14, ((ilogb (wartość) == ((int) 0x80000000))) = false Sukces
w = inf, ((ilogb (wartość) == ((int) 0x80000000))) = false Sukces

v = nan, (isunordered (1.2345, wartość)) = true Sukces
u = 3,14, (isunordered (1,2345, wartość)) = false Sukces
w = inf, (isunordered (1.2345, wartość)) = false Sukces

v = nan, (is_ieee754_nan (wartość)) = true Sukces
u = 3,14, (is_ieee754_nan (wartość)) = false Sukces
w = inf, (is_ieee754_nan (wartość)) = false Sukces

[C: \ my \ forums \ so \ 282 (wykryj NaN)]
> g ++ foo.cpp -ffast-math && a
Kompilator twierdzi, że IEEE 754 = true

v = nan, (std :: isnan (wartość)) = false FAILED
u = 3,14, (std :: isnan (wartość)) = false Sukces
w = inf, (std :: isnan (wartość)) = false Sukces

v = nan, ((fpclifyify (wartość) == 0x0100)) = false FAILED
u = 3,14, ((fpclassify (wartość) == 0x0100)) = fałszywy sukces
w = inf, ((fpclassify (wartość) == 0x0100)) = false Sukces

v = nan, ((wartość! = wartość)) = false FAILED
u = 3,14, ((wartość! = wartość)) = false Sukces
w = inf, ((wartość! = wartość)) = false Sukces

v = nan, ((wartość == Fp_info :: quiet_NaN ())) = true Sukces
u = 3,14, ((wartość == Fp_info :: quiet_NaN ())) = true FAILED
w = inf, ((wartość == Fp_info :: quiet_NaN ())) = true FAILED

v = nan, ((ilogb (wartość) == ((int) 0x80000000))) = true Sukces
u = 3,14, ((ilogb (wartość) == ((int) 0x80000000))) = false Sukces
w = inf, ((ilogb (wartość) == ((int) 0x80000000))) = false Sukces

v = nan, (isunordered (1.2345, wartość)) = false FAILED
u = 3,14, (isunordered (1,2345, wartość)) = false Sukces
w = inf, (isunordered (1.2345, wartość)) = false Sukces

v = nan, (is_ieee754_nan (wartość)) = true Sukces
u = 3,14, (is_ieee754_nan (wartość)) = false Sukces
w = inf, (is_ieee754_nan (wartość)) = false Sukces

[C: \ my \ forums \ so \ 282 (wykryj NaN)]
> _

Wyniki z Visual C ++:

[C: \ my \ forums \ so \ 282 (wykryj NaN)]
> cl / nologo- 2> i 1 | znajdź „++”
Microsoft (R) C / C ++ Optimization Compiler Version 19.00.23725 dla x86

[C: \ my \ forums \ so \ 282 (wykryj NaN)]
> cl foo.cpp / Feb && b
foo.cpp
Kompilator twierdzi, że IEEE 754 = true

v = nan, (std :: isnan (wartość)) = true Sukces
u = 3,14, (std :: isnan (wartość)) = false Sukces
w = inf, (std :: isnan (wartość)) = false Sukces

v = nan, ((fpclassify (wartość) == 2)) = true Sukces
u = 3,14, ((fpclassify (wartość) == 2)) = fałszywy sukces
w = inf, ((fpclassify (wartość) == 2)) = false Sukces

v = nan, ((wartość! = wartość)) = true Sukces
u = 3,14, ((wartość! = wartość)) = false Sukces
w = inf, ((wartość! = wartość)) = false Sukces

v = nan, ((wartość == Fp_info :: quiet_NaN ())) = false FAILED
u = 3,14, ((wartość == Fp_info :: quiet_NaN ())) = false Sukces
w = inf, ((wartość == Fp_info :: quiet_NaN ())) = false Sukces

v = nan, ((ilogb (wartość) == 0x7fffffff)) = true Sukces
u = 3,14, ((ilogb (wartość) == 0x7fffffff)) = fałszywy sukces
w = inf, ((ilogb (wartość) == 0x7fffffff)) = true FAILED

v = nan, (isunordered (1.2345, wartość)) = true Sukces
u = 3,14, (isunordered (1,2345, wartość)) = false Sukces
w = inf, (isunordered (1.2345, wartość)) = false Sukces

v = nan, (is_ieee754_nan (wartość)) = true Sukces
u = 3,14, (is_ieee754_nan (wartość)) = false Sukces
w = inf, (is_ieee754_nan (wartość)) = false Sukces

[C: \ my \ forums \ so \ 282 (wykryj NaN)]
> cl foo.cpp / Feb / fp: fast && b
foo.cpp
Kompilator twierdzi, że IEEE 754 = true

v = nan, (std :: isnan (wartość)) = true Sukces
u = 3,14, (std :: isnan (wartość)) = false Sukces
w = inf, (std :: isnan (wartość)) = false Sukces

v = nan, ((fpclassify (wartość) == 2)) = true Sukces
u = 3,14, ((fpclassify (wartość) == 2)) = fałszywy sukces
w = inf, ((fpclassify (wartość) == 2)) = false Sukces

v = nan, ((wartość! = wartość)) = true Sukces
u = 3,14, ((wartość! = wartość)) = false Sukces
w = inf, ((wartość! = wartość)) = false Sukces

v = nan, ((wartość == Fp_info :: quiet_NaN ())) = false FAILED
u = 3,14, ((wartość == Fp_info :: quiet_NaN ())) = false Sukces
w = inf, ((wartość == Fp_info :: quiet_NaN ())) = false Sukces

v = nan, ((ilogb (wartość) == 0x7fffffff)) = true Sukces
u = 3,14, ((ilogb (wartość) == 0x7fffffff)) = fałszywy sukces
w = inf, ((ilogb (wartość) == 0x7fffffff)) = true FAILED

v = nan, (isunordered (1.2345, wartość)) = true Sukces
u = 3,14, (isunordered (1,2345, wartość)) = false Sukces
w = inf, (isunordered (1.2345, wartość)) = false Sukces

v = nan, (is_ieee754_nan (wartość)) = true Sukces
u = 3,14, (is_ieee754_nan (wartość)) = false Sukces
w = inf, (is_ieee754_nan (wartość)) = false Sukces

[C: \ my \ forums \ so \ 282 (wykryj NaN)]
> _

Podsumowując powyższe wyniki, tylko bezpośrednie testowanie reprezentacji na poziomie bitów przy użyciu is_ieee754_nanfunkcji zdefiniowanej w tym programie testowym działało niezawodnie we wszystkich przypadkach zarówno z g ++, jak i Visual C ++.


Dodatek:
Po opublikowaniu powyższego dowiedziałem się o jeszcze jednym możliwym teście na NaN, wymienionym w innej odpowiedzi tutaj, mianowicie ((value < 0) == (value >= 0)). Okazało się, że działa dobrze z Visual C ++, ale zawiodło z -ffast-mathopcją g ++ . Tylko bezpośrednie testowanie bitatternów działa niezawodnie.

Pozdrawiam i hth. - Alf
źródło
7
inline bool IsNan(float f)
{
    const uint32 u = *(uint32*)&f;
    return (u&0x7F800000) == 0x7F800000 && (u&0x7FFFFF);    // Both NaN and qNan.
}

inline bool IsNan(double d)
{
    const uint64 u = *(uint64*)&d;
    return (u&0x7FF0000000000000ULL) == 0x7FF0000000000000ULL && (u&0xFFFFFFFFFFFFFULL);
}

Działa sizeof(int)to, jeśli ma 4 i sizeof(long long)8.

W czasie wykonywania jest to tylko porównanie, casting nie zajmuje żadnego czasu. Po prostu zmienia konfigurację flag porównania, aby sprawdzić równość.

ST3
źródło
Należy również pamiętać, że jest to ograniczone do reprezentacji IEEE 754.
Pozdrawiam i hth. - Alf
Zauważ, że ta obsada łamie ścisłą zasadę aliasingu g ++, a kompilator znany jest z wykonywania Unmentionable Things ™ po wykryciu formalnego UB. Zamiast wydajnych rzutowań, z g ++ musisz użyć memcpy, aby się upewnić, za pomocą tablicy bajtów. Kod na to w mojej odpowiedzi nr 2 .
Pozdrawiam i hth. - Alf
4

Możliwe rozwiązanie, które nie zależy od konkretnej reprezentacji IEEE dla NaN, byłoby następujące:

template<class T>
bool isnan( T f ) {
    T _nan =  (T)0.0/(T)0.0;
    return 0 == memcmp( (void*)&f, (void*)&_nan, sizeof(T) );
}
Dan Nathan
źródło
Zmienna zmiennoprzecinkowa o pojedynczej precyzji ma ponad 8 milionów uzasadnionych i różnych reprezentacji bitów dla NaN, więc musisz dodać więcej porównań. :)
Steve Hollasch
4

Biorąc pod uwagę, że (x! = X) nie zawsze jest gwarantowane dla NaN (np. Jeśli korzystam z opcji -ffast-matematyki), używałem:

#define IS_NAN(x) (((x) < 0) == ((x) >= 0))

Liczby nie mogą być jednocześnie <0 i> = 0, więc tak naprawdę to sprawdzenie jest pomyślne tylko wtedy, gdy liczba nie jest ani mniejsza, ani większa od lub równa zero. Który w zasadzie nie jest liczbą, ani NaN.

Możesz również użyć tego, jeśli wolisz:

#define IS_NAN(x) (!((x)<0) && !((x)>=0)

Nie jestem jednak pewien, jak wpływa na to matematyka, więc przebieg może się różnić.

Jerramy
źródło
Jest to w rzeczywistości tak samo f != fwadliwe, jak i wada. Widziałem, jak lvvm optymalizuje niemal identyczny fragment kodu. Optymalizator może rozpowszechniać informacje o pierwszym porównaniu i dowiedzieć się, że drugie porównanie może nigdy nie być prawdziwe, jeśli jest to pierwsze. (jeśli kompilator ściśle przestrzega zasad IEEE i tak f != fjest znacznie prostszy)
Markus
Nie działa z -ffast-mathopcją g ++ . Działa z Visual C ++. Zobacz ( stackoverflow.com/a/42138465/464581 ).
Pozdrawiam i hth. - Alf
3

Jeśli chodzi o mnie, rozwiązaniem może być makro, które uczyni je wyraźnie wbudowanym, a zatem wystarczająco szybkim. Działa również dla każdego typu pływaka. Opiera się na fakcie, że jedynym przypadkiem, gdy wartość nie jest równa samej sobie, jest to, że wartość nie jest liczbą.

#ifndef isnan
  #define isnan(a) (a != a)
#endif
użytkownik1705817
źródło
To jedna z najlepszych odpowiedzi na to pytanie! Dziękuję za podzielenie się.
Henri Menke,
2
Inne odpowiedzi wskazują, że może się to nie powieść przy ustawionej opcji -ffast-matematyki.
Technophile
3

To działa:

#include <iostream>
#include <math.h>
using namespace std;

int main ()
{
  char ch='a';
  double val = nan(&ch);
  if(isnan(val))
     cout << "isnan" << endl;

  return 0;
}

wyjście: isnan

edW
źródło
1

Wydaje mi się, że najlepszym prawdziwie wieloplatformowym podejściem byłoby użycie unii i przetestowanie wzorca bitowego podwójnego w celu sprawdzenia NaN.

Nie przetestowałem dokładnie tego rozwiązania i może istnieć bardziej wydajny sposób pracy z wzorcami bitów, ale myślę, że powinien on działać.

#include <stdint.h>
#include <stdio.h>

union NaN
{
    uint64_t bits;
    double num;
};

int main()
{
    //Test if a double is NaN
    double d = 0.0 / 0.0;
    union NaN n;
    n.num = d;
    if((n.bits | 0x800FFFFFFFFFFFFF) == 0xFFFFFFFFFFFFFFFF)
    {
        printf("NaN: %f", d);
    }

    return 0;
}
Sheldon Juncker
źródło
Zauważ, że „czytanie od członka związku, który nie był ostatnio napisany, jest niezdefiniowanym zachowaniem”. Tak więc użycie unionznaku typu pun dla dwóch typów może nie działać zgodnie z oczekiwaniami (: sad_panda :). Prawidłowym (choć nie do końca tak przenośnym, jak to pożądane) byłoby całkowite uniknięcie unii i zrobienie memcpy z doubleinnej uint64_tzmiennej, a następnie wykonanie testu przy użyciu tej zmiennej pomocniczej.
Eljay
0

Na x86-64 możesz mieć niezwykle szybkie metody sprawdzania NaN i nieskończoności, które działają niezależnie od -ffast-mathopcji kompilatora. ( f != f, std::isnan, std::isinfZawsze dają falsesię -ffast-math).


Testowanie NaN, liczb nieskończoności i skończonych można łatwo wykonać, sprawdzając maksymalny wykładnik potęgi. nieskończoność to maksymalny wykładnik z zerową mantysą, NaN to maksymalny wykładnik i niezerowa mantysa. Wykładnik jest przechowywany w następnych bitach po bicie najwyższego znaku, dzięki czemu możemy po prostu w lewo przesunąć, aby pozbyć się bitu znaku i uczynić wykładnik najwyższymi bitami, nie operator&jest konieczne maskowanie ( ):

static inline uint64_t load_ieee754_rep(double a) {
    uint64_t r;
    static_assert(sizeof r == sizeof a, "Unexpected sizes.");
    std::memcpy(&r, &a, sizeof a); // Generates movq instruction.
    return r;
}

static inline uint32_t load_ieee754_rep(float a) {
    uint32_t r;
    static_assert(sizeof r == sizeof a, "Unexpected sizes.");
    std::memcpy(&r, &a, sizeof a); // Generates movd instruction.
    return r;
}

constexpr uint64_t inf_double_shl1 = UINT64_C(0xffe0000000000000);
constexpr uint32_t inf_float_shl1 = UINT32_C(0xff000000);

// The shift left removes the sign bit. The exponent moves into the topmost bits,
// so that plain unsigned comparison is enough.
static inline bool isnan2(double a)    { return load_ieee754_rep(a) << 1  > inf_double_shl1; }
static inline bool isinf2(double a)    { return load_ieee754_rep(a) << 1 == inf_double_shl1; }
static inline bool isfinite2(double a) { return load_ieee754_rep(a) << 1  < inf_double_shl1; }
static inline bool isnan2(float a)     { return load_ieee754_rep(a) << 1  > inf_float_shl1; }
static inline bool isinf2(float a)     { return load_ieee754_rep(a) << 1 == inf_float_shl1; }
static inline bool isfinite2(float a)  { return load_ieee754_rep(a) << 1  < inf_float_shl1; }

Te stdwersje isinfi isfiniteobciążenia 2 double/floatstałe z .datasegmentu aw najgorszym przypadku mogą one powodować 2 tęskni pamięci podręcznej danych. Powyższe wersje nie ładują żadnych danych, inf_double_shl1a inf_float_shl1stałe są kodowane jako bezpośrednie operandy w instrukcjach montażu.


Szybsze isnan2są tylko 2 instrukcje montażu:

bool isnan2(double a) {
    bool r;
    asm(".intel_syntax noprefix"
        "\n\t ucomisd %1, %1"
        "\n\t setp %b0"
        "\n\t .att_syntax prefix"
        : "=g" (r)
        : "x" (a)
        : "cc"
        );
    return r;
}

Wykorzystuje fakt, że ucomisdinstrukcja ustawia flagę parzystości, jeśli dowolnym argumentem jest NaN. Tak std::isnandziała, gdy nie -ffast-mathokreślono żadnych opcji.

Maxim Egorushkin
źródło
-1

Standard IEEE mówi, że gdy wykładnikiem są wszystkie 1s, a mantysa nie jest równa zero, liczba jest a NaN. Double to 1bit znaku, 11bity wykładnikowe i 52bity mantysy. Sprawdź trochę.

bop
źródło
-3

Jak wspomniano powyżej, a! = A nie będzie działać w g ++ i niektórych innych kompilatorach, ale ta sztuczka powinna. To może nie być tak wydajne, ale wciąż jest sposób:

bool IsNan(float a)
{
    char s[4];
    sprintf(s, "%.3f", a);
    if (s[0]=='n') return true;
    else return false;
}

Zasadniczo, w g ++ (nie jestem pewien co do innych) printf wypisuje „nan” w formatach% d lub% .f, jeśli zmienna nie jest poprawną liczbą całkowitą / zmiennoprzecinkową. Dlatego ten kod sprawdza, czy pierwszym znakiem ciągu jest „n” (jak w „nan”)

ZenJ
źródło
2
Czy nie spowodowałoby to przepełnienia bufora, jeśli a = 234324.0f?
Mazyod
Tak, lub 340282346638528859811704183484516925440.000jeśli a = FLT_MAX. Będzie musiał użyć char s[7]; sprintf(s, "%.0g", a);, co będzie 6 chrs, jeśli a=-FLT_MAX, lub-3e+38
bobobobo
-3

To wykrywa nieskończoność, a także NaN w Visual Studio, sprawdzając, czy jest w podwójnych granicach:

//#include <float.h>
double x, y = -1.1; x = sqrt(y);
if (x >= DBL_MIN && x <= DBL_MAX )
    cout << "DETECTOR-2 of errors FAILS" << endl;
else
    cout << "DETECTOR-2 of errors OK" << endl;
mathengineer
źródło
Sprawdź definicję FLT_MIN, DBL_MINa LDBL_MINbardziej dokładnie. Są to najmniejsze znormalizowane wartości dla każdego typu. Na przykład pojedyncza precyzja ma ponad 8 milionów uzasadnionych wartości denorm, które są większe od zera i mniejsze niż FLT_MIN(i nie są NaN).
Steve Hollasch,