Sprawdzanie, czy double (lub float) to NaN w C++
Czy istnieje funkcja isnan ()?
PS.: I ' m in MinGW (if that makes a difference).
Rozwiązałem to używając isnan() z <math.h>
, która nie istnieje w <cmath>
, którą na początku byłem #include
.
20 answers
Zgodnie ze STANDARDEM IEEE, wartości NaN mają tę dziwną właściwość, że porównania z nimi są Zawsze false. Oznacza to, że dla zmiennoprzecinkowego f, f != f
będzie true tylko Jeśli f jest NaN.
Zauważ, że jak zauważyły niektóre komentarze poniżej, nie wszystkie Kompilatory przestrzegają tego przy optymalizacji kodu.
Dla każdego kompilatora, który twierdzi, że używa zmiennoprzecinkowego IEEE, ta sztuczka powinna zadziałać. Ale nie mogę zagwarantować, że to zadziała w praktyce. Sprawdź z Twój kompilator, jeśli masz wątpliwości.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-26 16:28:20
Nie ma funkcji isnan()
dostępnej w bieżącej bibliotece standardowej C++. Została wprowadzona w C99 i zdefiniowana jako makro, a nie Funkcja. Elementy biblioteki standardowej 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 nr 1. Tr1 wprowadza kompatybilność z C99 do C++. Pomimo faktu, że nigdy nie został oficjalnie przyjęty do standardu C++, wiele (GCC 4.0+ lub Visual C++ 9.0+ implementacje C++ zapewniają funkcje TR1, wszystkie z nich lub tylko niektóre (Visual C++ 9.0 nie dostarcza funkcji matematycznych C99).
Jeśli tr1 jest dostępny, to cmath
zawiera elementy C99 takie jakisnan()
, isfinite()
, itd. ale są one definiowane jako funkcje, a nie makra, zwykle w przestrzeni nazw std::tr1::
, chociaż wiele implementacji (np. GCC 4+ na Linuksie lub w XCode na Mac OS X 10.5+) wstrzykuje je bezpośrednio do std::
, więc std::isnan
jest dobrze zdefiniowany.
Co więcej, niektóre implementacje C++ nadal udostępniają makro C99 isnan()
dla C++ (dołączone przez cmath
lub math.h
), co może powodować więcej nieporozumień i programiści mogą założyć, że jest to standardowe zachowanie.
Uwaga o Viusal C++, jak wspomniano powyżej, nie dostarcza std::isnan
ani std::tr1::isnan
, ale dostarcza funkcję rozszerzenia zdefiniowaną jako _isnan()
, która jest dostępna od Visual C++ 6.0
Na XCode jest jeszcze więcej zabawy. Jak wspomniano, GCC 4 + definiuje std::isnan
. Dla starszych wersji kompilatora i biblioteki Xcode, wydaje się (tutaj istotna dyskusja ), nie miałem okazji sprawdzić) dwie funkcje są zdefiniowane, {[14] } na Intel i __isnand()
NA Power PC.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-23 16:19:05
Pierwsze rozwiązanie: jeśli używasz C++11
Ponieważ zapytano o to, pojawiło się trochę nowych rozwiązań: ważne jest, aby wiedzieć, że std::isnan()
jest częścią C++11
Synopsis
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ą a (NaN
).
Parametry
arg
: wartość zmiennoprzecinkowa
Return value
true
jeśli arg jest NaN
, false
inaczej
Numer referencyjny
Http://en.cppreference.com/w/cpp/numeric/math/isnan
Zwróć uwagę, że jest to niezgodne z-fast-math, jeśli używasz g++, zobacz poniżej inne sugestie.
Inne rozwiązania: jeśli używasz narzędzi niezgodnych z C++11
Dla C99, w C, jest to zaimplementowane jako makro isnan(c)
, które zwraca wartość int. Typ x
jest zmiennoprzecinkowy, podwójny lub długi podwójny.
isnan()
.
Rzekomo przenośnym sposobem sprawdzania NaN
jest użycie właściwości IEEE 754, która NaN
nie jest sobie równa: tzn. x == x
będzie fałszywa dla x
jako NaN
.
Ostatnia opcja może jednak nie działać z każdym kompilatorem i niektórymi ustawieniami( szczególnie ustawieniami optymalizacji), więc w ostateczności zawsze można sprawdzić wzorzec bitowy ...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-10 18:13:18
W Boost istnieje również biblioteka tylko nagłówka , która ma schludne narzędzia do obsługi zmiennoprzecinkowych typów danych
#include <boost/math/special_functions/fpclassify.hpp>
Otrzymujesz 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, spójrz na cały zestaw narzędzi matematycznych z Boost, ma wiele przydatnych narzędzi i szybko rośnie.
Również w przypadku punktów zmiennoprzecinkowych i nieprzecinkowych dobrym pomysłem może być przyjrzenie się Konwersjom liczbowym .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-22 18:52:22
Istnieją trzy "oficjalne" sposoby: posix isnan
makro , c++0x isnan
function template , lub visual c++ _isnan
function .
I niestety, nie ma niezawodnego sposobu, aby wykryć, czy masz reprezentację IEEE 754 z Nan. Biblioteka standardowa oferuje oficjalny taki sposób (numeric_limits<double>::is_iec559
). Ale w praktyce Kompilatory takie jak g++ to psują.
Teoretycznie można by użyć po prostu x != x
, ale Kompilatory takie jak g++ i visual c++ to psują.
Więc w końcu, test na konkretne NaN bitpatterns , zakładając (i miejmy nadzieję, egzekwowanie, w pewnym momencie!) szczególną reprezentację, np. IEEE 754.
EDIT : jako przykład "kompilatorów takich jak g++ ... screw that up", 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 );
}
Kompilowanie z g++ (TDM-2 mingw32) 4.4.1:
C:\test> type "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> a && echo works... || echo !failed works... C:\test> gnuc x.cpp --fast-math C:\test> a && echo works... || echo !failed Assertion failed: a != b, file x.cpp, line 6 This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information. !failed C:\test> _
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-07 21:00:58
Istnieje STD:: isnan, jeśli kompilator obsługuje rozszerzenia c99, ale nie jestem pewien, czy MinGW tak.
Oto mała funkcja, która powinna działać, jeśli kompilator nie ma standardowej funkcji:
bool custom_isnan(double var)
{
volatile double d = var;
return d != d;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-20 18:14:58
Możesz użyć numeric_limits<float>::quiet_NaN( )
zdefiniowanego w bibliotece standardowej limits
do testowania. Istnieje osobna stała zdefiniowana dla 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++ na Linuksie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-20 19:18:12
Możesz użyć funkcji isnan()
, 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 dostarczy tej funkcji, możesz również zdefiniować swój własny wariant zgodności.
inline bool isnan(double x) {
return x != x;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-07 09:34:32
Nan prevention
Moja odpowiedź na to pytanie brzmi nie używaj kontroli wstecznych dla nan
. prewencyjne sprawdzanie podziałów formularza 0.0/0.0
zamiast tego.
#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.
nan
wyniki operacji 0.f/0.f
lub 0.0/0.0
. nan
jest strasznym nemezis do stabilności kodu, który musi być wykryty i zapobiec bardzo ostrożnie1. Właściwości nan
, które różnią się od normalnych liczby:
-
nan
jest toksyczny, (5*nan
=nan
) -
nan
nie jest równa niczym, nawet sobie (nan
!=nan
) -
nan
nie większy niż cokolwiek (nan
!> 0) -
nan
jest nie mniej niż cokolwiek (nan
!
Ostatnie 2 właściwości są przeciwnormatywne i spowodują dziwne zachowanie kodu, które opiera się na porównaniu z liczbą nan
(3. ostatnia właściwość jest nieparzysta, ale prawdopodobnie nigdy nie zobaczysz x != x ?
w Twój kod (chyba że sprawdzasz nan (nierzetelnie))).
W moim własnym kodzie, zauważyłem, że {[2] } wartości zwykle wytwarzają trudne do znalezienia błędy. (Zauważ, że jest to , a nie przypadek inf
lub -inf
. (-inf
TRUE, ( 0 inf ) zwraca TRUE, a nawet (-inf
inf) zwraca TRUE. Więc, z mojego doświadczenia, zachowanie kodu jest często {[55] } nadal zgodnie z życzeniem).
Co robić pod nan
What you want to happen under 0.0/0.0
musi być traktowany jako specjalny przypadek , ale to, co robisz, musi zależeć od liczb, które spodziewasz się wyjść z kodu.
W powyższym przykładzie, wynikiem (0.f/FLT_MIN
) będzie 0
, W zasadzie. Możesz zamiast tego chcieć 0.0/0.0
wygenerować HUGE
.
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 w powyższym przypadku, gdyby x było 0.f
, inf
wynikałoby (co ma całkiem dobre/nieniszczące zachowanie, jak wspomniano powyżej).
Remember, integer dzielenie przez 0 powoduje runtime wyjątek . Więc zawsze musisz sprawdzić podział liczby całkowitej przez 0. To, że 0.0/0.0
po cichu ocenia na nan
, nie oznacza, że możesz być leniwy i nie sprawdzać 0.0/0.0
, zanim to się stanie.
1 sprawdzanie nan
przez x != x
jest czasami zawodne (x != x
jest usuwane przez niektóre Kompilatory optymalizujące, które łamią zgodność z IEEE, szczególnie gdy włączony jest przełącznik -ffast-math
).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:03:05
Poniższy kod używa definicji NAN (zbiór wszystkich bitów wykładniczych, co najmniej jeden zestaw bitów ułamkowych) i zakłada, że sizeof(int) = sizeof(float) = 4. Możesz poszukać NAN w Wikipedii po szczegóły.
bool IsNan( float value )
{
return ((*(UINT*)&value) & 0x7fffffff) > 0x7f800000;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-05 01:53:33
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);
}
To działa, Jeśli sizeof(int)
jest 4 i {[2] } jest 8.
W czasie pracy jest to tylko porównanie, odlewy nie zajmują czasu. Po prostu zmienia konfigurację FLAG porównawczych, aby sprawdzić równość.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-19 21:56:13
W C++14 istnieje wiele sposobów na sprawdzenie, czy Liczba zmiennoprzecinkowa value
jest NaN.
Z TYCH SPOSOBÓW, tylko sprawdzanie bitów reprezentacji liczby,
działa niezawodnie, jak zaznaczono w mojej pierwotnej odpowiedzi. W szczególności std::isnan
i często proponowane sprawdzenie v != v
nie działają niezawodnie i nie powinny być używane, aby Twój kod nie przestał działać poprawnie, gdy ktoś zdecyduje, że optymalizacja zmiennoprzecinkowa jest potrzebna i poprosi kompilator o to. Ta sytuacja może się zmienić, Kompilatory mogą uzyskać bardziej zgodne, ale dla tego problemu, który nie wystąpił w ciągu 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 upvoted odpowiedź zalecającą niewiarygodne v != v
test został wybrany. Stąd ta dodatkowa, bardziej aktualna odpowiedź (obecnie mamy na horyzoncie standardy C++11 I C++14 oraz C++17).
Główne sposoby sprawdzania NaN-ness, od C++14, to:
std::isnan(value) )
jest zamierzoną biblioteką standardową od C++11.isnan
najwyraźniej koliduje z Makro Posix o tej samej nazwie, ale w praktyce nie stanowi to problemu. Głównym problemem jest że gdy wymagana jest optymalizacja arytmetyczna zmiennoprzecinkowa, to z co najmniej jednym głównym kompilatorem, a mianowicie g++,std::isnan
zwracafalse
dla argumentu NaN .(fpclassify(value) == FP_NAN) )
Cierpi na ten sam problem costd::isnan
, tzn. nie jest niezawodny.(value != value) )
Polecane w wielu Tak odpowiedzi. Cierpi na ten sam problem costd::isnan
, czyli, nie jest wiarygodny.(value == Fp_info::quiet_NaN()) )
Jest to test, który przy standardowym zachowaniu nie powinien wykrywać Nan, ale z zoptymalizowane zachowanie może wykrywać Nan (ze względu na zoptymalizowany kod porównujący bitlevel bezpośrednio), a być może w połączeniu z innym sposobem na obejmuje standardowe Nie zoptymalizowane zachowanie, może niezawodnie wykryć NaN. Niestety okazało się, że nie działa niezawodnie.(ilogb(value) == FP_ILOGBNAN) )
[4]}, tzn. nie jest wiarygodny.isunordered(1.2345, value) )
[4]}, tzn. nie jest wiarygodny.is_ieee754_nan( value ) )
To nie jest standardowa funkcja. Sprawdza bity zgodnie z IEEE 754 standard. Jest całkowicie niezawodny , ale kod jest w pewnym stopniu zależny od systemu.
W poniższy kompletny kod testowy "success" określa, czy wyrażenie zgłasza Nan-ness wartości. Dla większości wyrażeń ta miara sukcesu, cel wykrywania Nan i tylko Nan, odpowiada ich standardowej semantyce. W przypadku wyrażenia (value == Fp_info::quiet_NaN()) )
standardowym zachowaniem jest jednak to, ż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ż jeszcze raz, że standardowe zachowanie (value == Fp_info::quiet_NaN())
jest takie, że nie działa on jako detektor NaN, jest po prostu bardzo praktyczny zainteresowanie tutaj):
[C:\my\forums\so\282 (detect NaN)] > g++ --version | find "++" g++ (x86_64-win32-sjlj-rev1, Built by MinGW-W64 project) 6.3.0 [C:\my\forums\so\282 (detect NaN)] > g++ foo.cpp && a Compiler claims IEEE 754 = true v = nan, (std::isnan(value)) = true Success u = 3.14, (std::isnan(value)) = false Success w = inf, (std::isnan(value)) = false Success v = nan, ((fpclassify(value) == 0x0100)) = true Success u = 3.14, ((fpclassify(value) == 0x0100)) = false Success w = inf, ((fpclassify(value) == 0x0100)) = false Success v = nan, ((value != value)) = true Success u = 3.14, ((value != value)) = false Success w = inf, ((value != value)) = false Success v = nan, ((value == Fp_info::quiet_NaN())) = false FAILED u = 3.14, ((value == Fp_info::quiet_NaN())) = false Success w = inf, ((value == Fp_info::quiet_NaN())) = false Success v = nan, ((ilogb(value) == ((int)0x80000000))) = true Success u = 3.14, ((ilogb(value) == ((int)0x80000000))) = false Success w = inf, ((ilogb(value) == ((int)0x80000000))) = false Success v = nan, (isunordered(1.2345, value)) = true Success u = 3.14, (isunordered(1.2345, value)) = false Success w = inf, (isunordered(1.2345, value)) = false Success v = nan, (is_ieee754_nan( value )) = true Success u = 3.14, (is_ieee754_nan( value )) = false Success w = inf, (is_ieee754_nan( value )) = false Success [C:\my\forums\so\282 (detect NaN)] > g++ foo.cpp -ffast-math && a Compiler claims IEEE 754 = true v = nan, (std::isnan(value)) = false FAILED u = 3.14, (std::isnan(value)) = false Success w = inf, (std::isnan(value)) = false Success v = nan, ((fpclassify(value) == 0x0100)) = false FAILED u = 3.14, ((fpclassify(value) == 0x0100)) = false Success w = inf, ((fpclassify(value) == 0x0100)) = false Success v = nan, ((value != value)) = false FAILED u = 3.14, ((value != value)) = false Success w = inf, ((value != value)) = false Success v = nan, ((value == Fp_info::quiet_NaN())) = true Success u = 3.14, ((value == Fp_info::quiet_NaN())) = true FAILED w = inf, ((value == Fp_info::quiet_NaN())) = true FAILED v = nan, ((ilogb(value) == ((int)0x80000000))) = true Success u = 3.14, ((ilogb(value) == ((int)0x80000000))) = false Success w = inf, ((ilogb(value) == ((int)0x80000000))) = false Success v = nan, (isunordered(1.2345, value)) = false FAILED u = 3.14, (isunordered(1.2345, value)) = false Success w = inf, (isunordered(1.2345, value)) = false Success v = nan, (is_ieee754_nan( value )) = true Success u = 3.14, (is_ieee754_nan( value )) = false Success w = inf, (is_ieee754_nan( value )) = false Success [C:\my\forums\so\282 (detect NaN)] > _
Wyniki z Visual C++:
[C:\my\forums\so\282 (detect NaN)] > cl /nologo- 2>&1 | find "++" Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23725 for x86 [C:\my\forums\so\282 (detect NaN)] > cl foo.cpp /Feb && b foo.cpp Compiler claims IEEE 754 = true v = nan, (std::isnan(value)) = true Success u = 3.14, (std::isnan(value)) = false Success w = inf, (std::isnan(value)) = false Success v = nan, ((fpclassify(value) == 2)) = true Success u = 3.14, ((fpclassify(value) == 2)) = false Success w = inf, ((fpclassify(value) == 2)) = false Success v = nan, ((value != value)) = true Success u = 3.14, ((value != value)) = false Success w = inf, ((value != value)) = false Success v = nan, ((value == Fp_info::quiet_NaN())) = false FAILED u = 3.14, ((value == Fp_info::quiet_NaN())) = false Success w = inf, ((value == Fp_info::quiet_NaN())) = false Success v = nan, ((ilogb(value) == 0x7fffffff)) = true Success u = 3.14, ((ilogb(value) == 0x7fffffff)) = false Success w = inf, ((ilogb(value) == 0x7fffffff)) = true FAILED v = nan, (isunordered(1.2345, value)) = true Success u = 3.14, (isunordered(1.2345, value)) = false Success w = inf, (isunordered(1.2345, value)) = false Success v = nan, (is_ieee754_nan( value )) = true Success u = 3.14, (is_ieee754_nan( value )) = false Success w = inf, (is_ieee754_nan( value )) = false Success [C:\my\forums\so\282 (detect NaN)] > cl foo.cpp /Feb /fp:fast && b foo.cpp Compiler claims IEEE 754 = true v = nan, (std::isnan(value)) = true Success u = 3.14, (std::isnan(value)) = false Success w = inf, (std::isnan(value)) = false Success v = nan, ((fpclassify(value) == 2)) = true Success u = 3.14, ((fpclassify(value) == 2)) = false Success w = inf, ((fpclassify(value) == 2)) = false Success v = nan, ((value != value)) = true Success u = 3.14, ((value != value)) = false Success w = inf, ((value != value)) = false Success v = nan, ((value == Fp_info::quiet_NaN())) = false FAILED u = 3.14, ((value == Fp_info::quiet_NaN())) = false Success w = inf, ((value == Fp_info::quiet_NaN())) = false Success v = nan, ((ilogb(value) == 0x7fffffff)) = true Success u = 3.14, ((ilogb(value) == 0x7fffffff)) = false Success w = inf, ((ilogb(value) == 0x7fffffff)) = true FAILED v = nan, (isunordered(1.2345, value)) = true Success u = 3.14, (isunordered(1.2345, value)) = false Success w = inf, (isunordered(1.2345, value)) = false Success v = nan, (is_ieee754_nan( value )) = true Success u = 3.14, (is_ieee754_nan( value )) = false Success w = inf, (is_ieee754_nan( value )) = false Success [C:\my\forums\so\282 (detect NaN)] > _
Podsumowując powyższe wyniki, tylko bezpośrednie testowanie reprezentacji na poziomie bitowym, przy użyciu funkcji is_ieee754_nan
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 innej możliwej do przetestowania dla NaN, wymienionej w innej odpowiedzi tutaj, a mianowicie ((value < 0) == (value >= 0))
. Okazało się, że działa dobrze z wizualnym C++ , ale nie udało się z opcją -ffast-math
g++. Tylko bezpośrednie testowanie bitpattern działa niezawodnie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:05
Możliwe rozwiązanie, które nie zależałoby 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) );
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-03 22:03:16
Jak dla mnie rozwiązaniem może być makro, aby było wyraźnie inline, a tym samym wystarczająco szybkie. Działa również dla każdego typu float. Opiera się na fakcie, że jedynym przypadkiem, gdy wartość nie jest równa sama w sobie, jest wtedy, gdy wartość nie jest liczbą.
#ifndef isnan
#define isnan(a) (a != a)
#endif
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-11 09:08:56
Po przeczytaniu innych odpowiedzi chciałem czegoś, co przejdzie przez ostrzeżenie o porównywaniu zmiennoprzecinkowym i nie złamie się pod szybką matematyką. Następujący kod wydaje się działać:
/*
Portable warning-free NaN test:
* Does not emit warning with -Wfloat-equal (does not use float comparisons)
* Works with -O3 -ffast-math (floating-point optimization)
* Only call to standard library is memset and memcmp via <cstring>
* Works for IEEE 754 compliant floating-point representations
* Also works for extended precision long double
*/
#include <cstring>
template <class T> bool isNaN(T x)
{
/*Initialize all bits including those used for alignment to zero. This sets
all the values to positive zero but does not clue fast math optimizations as
to the value of the variables.*/
T z[4];
memset(z, 0, sizeof(z));
z[1] = -z[0];
z[2] = x;
z[3] = z[0] / z[2];
/*Rationale for following test:
* x is 0 or -0 --> z[2] = 0, z[3] = NaN
* x is a negative or positive number --> z[3] = 0
* x is a negative or positive denormal number --> z[3] = 0
* x is negative or positive infinity --> z[3] = 0
(IEEE 754 guarantees that 0 / inf is zero)
* x is a NaN --> z[3] = NaN != 0.
*/
//Do a bitwise comparison test for positive and negative zero.
bool z2IsZero = memcmp(&z[2], &z[0], sizeof(T)) == 0 ||
memcmp(&z[2], &z[1], sizeof(T)) == 0;
bool z3IsZero = memcmp(&z[3], &z[0], sizeof(T)) == 0 ||
memcmp(&z[3], &z[1], sizeof(T)) == 0;
//If the input is bitwise zero or negative zero, then it is not NaN.
return !z2IsZero && !z3IsZero;
}
//NaN test suite
#include <iostream>
/*If printNaN is true then only expressions that are detected as NaN print and
vice versa.*/
template <class T> void test(bool printNaN)
{
T v[10] = {-0.0, 0.0, -1.0, 1.0,
std::numeric_limits<T>::infinity(),
-std::numeric_limits<T>::infinity(),
std::numeric_limits<T>::denorm_min(),
-std::numeric_limits<T>::denorm_min(),
std::numeric_limits<T>::quiet_NaN(),
std::numeric_limits<T>::signaling_NaN()};
for(int i = 0; i < 10; i++)
{
for(int j = 0; j < 10; j++)
{
if(isNaN(v[i] + v[j]) == printNaN)
std::cout << v[i] << "+" << v[j] << " = " << v[i] + v[j] << std::endl;
if(isNaN(v[i] - v[j]) == printNaN)
std::cout << v[i] << "-" << v[j] << " = " << v[i] - v[j] << std::endl;
if(isNaN(v[i] * v[j]) == printNaN)
std::cout << v[i] << "*" << v[j] << " = " << v[i] * v[j] << std::endl;
if(isNaN(v[i] / v[j]) == printNaN)
std::cout << v[i] << "/" << v[j] << " = " << v[i] / v[j] << std::endl;
}
}
}
//Test each floating-point type.
int main()
{
std::cout << "NaNs:" << std::endl;
test<float>(true);
test<double>(true);
test<long double>(true);
std::cout << std::endl << "Not NaNs:" << std::endl;
test<float>(false);
test<double>(false);
test<long double>(false);
return 0;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-07 07:05:57
Biorąc pod uwagę ,że (x != x) nie zawsze jest gwarantowane dla NaN( np. jeśli używam opcji-ffast-math), używam:
#define IS_NAN(x) (((x) < 0) == ((x) >= 0))
Liczby nie mogą być zarówno = 0, więc tak naprawdę ta kontrola przechodzi tylko wtedy, gdy liczba nie jest ani mniejsza, ani większa, ani równa zeru. Co w zasadzie nie ma numeru, albo NaN.
Możesz również użyć tego, jeśli wolisz:
#define IS_NAN(x) (!((x)<0) && !((x)>=0)
Nie jestem pewien, jak to wpływa na-ffast-math, więc twój przebieg może się różnić.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-26 01:06:53
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-03 22:38:29
Wydaje mi się, że najlepszym prawdziwie wieloplatformowym podejściem byłoby użycie Unii i przetestowanie wzorca bitowego sobowtóra w celu sprawdzenia Nan.
Nie testowałem dokładnie tego rozwiązania i może istnieje bardziej efektywny sposób pracy z wzorcami bitowymi, ale myślę, że powinno zadział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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-08 17:20:07
Standard IEEE mówi gdy wykładnik jest wszystkie 1s oraz mantissa nie jest zerem, numer to NaN. Double to 1 bit znakowy, 11 bitów wykładniczych i 52 bity mantysy. Sprawdź trochę.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-31 19:26:31
Jako komentarze powyżej stan a != a nie będzie działać w G++ i niektórych innych kompilatorach, ale ta sztuczka powinna. Może nie jest tak skuteczny, ale i tak 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' na %d lub %.formatuje f, jeśli zmienna nie jest poprawną liczbą całkowitą / zmienną zmienną. Dlatego ten kod sprawdza, czy pierwszy znak łańcucha jest " n " (jak w "nan")
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-26 21:33:59