Проверка, является ли double (или float) NaN в C++
есть ли функция isnan ()?
PS.: Я в MinGW (если это имеет значение).
я решил это, используя isnan () из <math.h>
, которого не существует в <cmath>
, который находился #include
ing сначала.
20 ответов
согласно стандарту IEEE, значения NaN имеют нечетное свойство, что сравнения с ними являются всегда false. То есть для поплавка f,f != f
будет true только если f-NaN.
обратите внимание, что, как указано в некоторых комментариях ниже, не все компиляторы уважают это при оптимизации кода.
для любого компилятора, который утверждает, что использует плавающую точку IEEE, этот трюк должны работа. Но я не могу гарантировать, что это будет работать на практике. Если сомневаетесь, обратитесь к компилятору.
нет isnan()
функция доступна в текущей стандартной библиотеке C++. Он был введен в C99 и макрос не функция. Элементы стандартной библиотеки, определенные C99, не являются частью текущего стандарта C++ ISO / IEC 14882:1998 и его обновления ISO/IEC 14882: 2003.
в 2005 году был предложен технический доклад 1. TR1 обеспечивает совместимость с C99 на C++. Несмотря на то, что он никогда официально не был принят, чтобы стать Стандарт C++, много (GCC 4.0+ или Visual C++ 9.0+ реализации C++ предоставляют функции TR1, все или только некоторые (Visual C++ 9.0 не предоставляет математические функции C99).
если TR1 доступен, то cmath
включает элементы C99, такие как isnan()
, isfinite()
, etc. но они определяются как функции, а не макросы, обычно в std::tr1::
пространство имен, хотя многие реализации (например, GCC 4+ на Linux или в XCode на Mac OS X 10.5+) вводят их непосредственно в std::
, так что std::isnan
хорошо определенными.
более того, некоторые реализации C++ по-прежнему делают c99 isnan()
макрос доступен для C++ (включен через cmath
или math.h
), что может вызвать больше путаницы, и разработчики могут предположить, что это стандартное поведение.
примечание о Viusal c++, Как упоминалось выше, оно не предоставляет std::isnan
ни std::tr1::isnan
, но он предоставляет функцию расширения, определенную как _isnan()
который был доступен с Visual C++ 6.0
на XCode, есть еще больше удовольствия. Как уже упоминалось, GCC 4 + определяет std::isnan
. Для более старых версий компилятора и библиотеки форма XCode, кажется (вот обсуждения), не имел возможности проверить себя) определены две функции,__inline_isnand()
на Intel и __isnand()
на ПК силы.
первое решение: если вы используете C++11
поскольку это было задано, было немного новых событий: важно знать, что std::isnan()
является частью C++11
справка
определены в заголовке <cmath>
bool isnan( float arg ); (since C++11)
bool isnan( double arg ); (since C++11)
bool isnan( long double arg ); (since C++11)
определяет, если заданное число с плавающей запятой arg не является числом (NaN
).
параметры
arg
: с плавающей запятой
вернуться значение
true
если arg NaN
, false
иначе
ссылка
http://en.cppreference.com/w/cpp/numeric/math/isnan
обратите внимание, что это несовместимо с-fast-math если вы используете g++, см. Ниже другие предложения.
другие решения: если вы используете не совместимые с C++11 инструменты
для C99, в C, это реализовано как макрос isnan(c)
это возвращает значение int. Тип x
должно быть числом с плавающей точкой, двойной или двойной.
различные поставщики могут включать или не включать функцию isnan()
.
предположительно портативный способ проверить NaN
использовать свойство IEEE 754, которое NaN
не равен самому себе, т. е. x == x
будет ложным для x
будучи NaN
.
однако последний вариант может не работать с каждым компилятором и некоторыми настройками (в частности настройки оптимизации), поэтому в крайнем случае вы всегда можете проверить битовый шаблон ...
также библиотека только для заголовков присутствует в Boost, которые имеют аккуратные инструменты для работы с типами данных с плавающей запятой
#include <boost/math/special_functions/fpclassify.hpp>
Вы получаете следующие функции:
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);
Если у вас есть время, то посмотрите на весь математический инструментарий от Boost, он имеет много полезных инструментов и быстро растет.
также при работе с плавающими и неплавающими точками может быть хорошей идеей посмотреть на числовой Конверсии.
существует три "официальных" способа: posix isnan
макрос, c++0x isnan
шаблон функции, или visual C++ _isnan
функции.
к сожалению, довольно непрактично определить, какой из них использовать.
и, к сожалению, нет надежного способа определить, есть ли у вас представление IEEE 754 с NaNs. Стандартная библиотека предлагает официальный такой способ (numeric_limits<double>::is_iec559
). Но на практике компиляторы, такие как G++ винта это.
теоретически можно было бы использовать просто x != x
, но компиляторы, такие как g++ и visual c++, ошибаются.
так, в конце концов, тест для конкретного NaN bitpatterns, предполагая (и, надеюсь, принуждая, в какой-то момент!) конкретное представление, такое как IEEE 754.
редактировать: в качестве примера "компиляторов, таких как g++ ... вверните это", рассмотрим
#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 );
}
компиляция с 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> _
существует std:: isnan, если компилятор поддерживает расширения c99, но я не уверен, что mingw делает.
вот небольшая функция, которая должна работать, если ваш компилятор не имеет стандартной функции:
bool custom_isnan(double var)
{
volatile double d = var;
return d != d;
}
можно использовать numeric_limits<float>::quiet_NaN( )
определена в limits
стандартная библиотека для тестирования. Существует отдельная константа, определенная для 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;
}
Я не знаю, работает ли это на всех платформах, так как я тестировал только с g++ на Linux.
можно использовать isnan()
функция, но вам нужно включить математическую библиотеку C.
#include <cmath>
поскольку эта функция является частью C99, она доступна не везде. Если поставщик не предоставляет функцию, можно также определить собственный вариант для совместимости.
inline bool isnan(double x) {
return x != x;
}
профилактика НАНА
мой ответ на этот вопрос не используйте обратную проверки nan
. Использовать превентивные проверяет деления формы 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.
nan
результаты работы 0.f/0.f
или 0.0/0.0
. nan
является ужасным возмездием стабильности вашего кода, который должен быть обнаружен и предупредить внимательно1. Свойства nan
, которые отличаются от нормальных чисел:
-
nan
ядовит, (5*nan
=nan
) -
nan
не равно ничему, даже самому себе (nan
!=nan
) -
nan
не более чем (nan
!> 0) -
nan
не меньше чем (nan
!
последние 2 перечисленных свойства являются контр-логическими и приведут к нечетному поведению кода, который полагается на сравнения с nan
номер (3-е последнее свойство тоже нечетное, но вы, вероятно, никогда не увидите x != x ?
в вашем коде (если вы не проверяете nan (ненадежно))).
в моем собственном коде я заметил, что nan
значения, как правило, трудно найти ошибки. (Обратите внимание, как это не в случае inf
или -inf
. (-inf
TRUE, ( 0 inf) возвращает TRUE и даже (-inf
inf) возвращает TRUE. Итак, по моему опыту, поведение кода часто еще как хотелось).
что делать под nan
что вы хотите, чтобы произошло под 0.0/0.0
должно обрабатываться как особый случай, но то, что вы делаете, должно зависеть от чисел, которые вы ожидаете выйти из кода.
в приведенном выше примере результат (0.f/FLT_MIN
) будет 0
, в основном. Вы можете 0.0/0.0
для создания . Итак,
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.
так в выше, если Х 0.f
, inf
приведет (что имеет довольно хорошее / неразрушающее поведение, как упоминалось выше на самом деле).
помните, что целочисленное деление на 0 вызывает исключение времени выполнения. Поэтому вы всегда должны проверять целочисленное деление на 0. Просто потому что 0.0/0.0
спокойно оценивает в nan
не означает, что вы можете быть ленивым и не проверять 0.0/0.0
прежде чем это произойдет.
1 проверка nan
via x != x
иногда ненадежен (x != x
удаляются некоторыми оптимизирующими компиляторами, которые нарушают соответствие IEEE, особенно когда -ffast-math
выключатель включен).
в следующем коде используется определение NAN(все экспоненциальные биты, по крайней мере, один дробный бит) и предполагается, что sizeof(int) = sizeof (float) = 4. Вы можете найти NAN в Википедии для деталей.
bool IsNan( float value )
{
return ((*(UINT*)&value) & 0x7fffffff) > 0x7f800000;
}
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);
}
это работает если sizeof(int)
4 и sizeof(long long)
в 8.
во время выполнения только сравнение, отливки не принимает никакое время. Он просто изменяет конфигурацию флагов сравнения, чтобы проверить равенство.
начиная с C++14 существует несколько способов проверить, является ли число с плавающей запятой value
- это Нэн.
из этих способов, только проверка битов записи числа ,
работает надежно, как отмечено в моем первоначальном ответе. В частности, std::isnan
и часто предлагаемую проверку v != v
, не работают надежно и не должны использоваться, чтобы ваш код перестает работать правильно, когда кто-то решает, что оптимизация с плавающей запятой необходима, и просит компилятор сделать это. Эта ситуация может измениться, компиляторы могут получить больше соответствия, но для этой проблемы, которая не произошла за 6 лет с момента первоначального ответа.
около 6 лет моим первоначальным ответом было выбранное решение для этого вопроса, которое было в порядке. Но в последнее время очень популярный ответ, рекомендующий ненадежный v != v
выбран тест. Следовательно, этот дополнительный более актуальный ответ (теперь у нас есть стандарты C++11 и C++14, А C++17 на горизонт.)
основными способами проверки NaN-ness, начиная с C++14, являются:
std::isnan(value) )
является предполагаемым стандартным способом библиотеки с C++11.isnan
видимо конфликты с Макрос в POSIX, но на практике это не проблема. Главная проблема что когда запрашивается арифметическая оптимизация с плавающей запятой, то хотя бы с одним основным компилятором, а именно g++,std::isnan
возвращаетfalse
для NaN аргумент.(fpclassify(value) == FP_NAN) )
Страдает от той же проблемы, что иstd::isnan
, т. е. не надежный.(value != value) )
Рекомендуется во многих ответах SO. Страдает от той же проблемы, что иstd::isnan
, т. е., не надежный.(value == Fp_info::quiet_NaN()) )
Это тест, который со стандартным поведением не должен обнаруживать NaNs, но это с оптимизированное поведение может обнаружить NaNs (из-за оптимизированного кода просто сравнивая bitlevel представления непосредственно), и, возможно, в сочетании с другим способом покройте стандартное не-оптимизированное поведение, смогл надежно обнаружить НАН. К сожалению оказалось, что он не работает надежно.(ilogb(value) == FP_ILOGBNAN) )
Страдает от той же проблемы, что иstd::isnan
, т. е. не надежный.isunordered(1.2345, value) )
Страдает от той же проблемы, что иstd::isnan
, т. е. не надежный.is_ieee754_nan( value ) )
Это не стандартная функция. Это проверка битов согласно IEEE 754 норматив. Это совершенно надежно но код несколько зависит от системы.
в следующем полном тестовом коде "успех" -это то, сообщает ли выражение Nan-ness значения. Для большинства выражений эта мера успеха, цель обнаружения NaNs и только NaNs, соответствует их стандартная семантика. Для (value == Fp_info::quiet_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 ) );
}
результаты с g++ (обратите внимание еще раз, что стандартное поведение (value == Fp_info::quiet_NaN())
это то, что он не работает как NaN-детектор, это просто очень практический интерес здесь):
[C:\my\forums\so2 (detect NaN)] > g++ --version | find "++" g++ (x86_64-win32-sjlj-rev1, Built by MinGW-W64 project) 6.3.0 [C:\my\forums\so2 (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\so2 (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\so2 (detect NaN)] > _
результаты с Visual C++:
[C:\my\forums\so2 (detect NaN)] > cl /nologo- 2>&1 | find "++" Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23725 for x86 [C:\my\forums\so2 (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\so2 (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\so2 (detect NaN)] > _
суммируя вышеуказанные результаты, только прямое тестирование представления битового уровня, используя is_ieee754_nan
функция, определенная в этой тестовой программе, надежно работала во всех случаях как с g++, так и с Visual C++.
дополнение:
После публикации выше я узнал еще об одном возможном тестировании для NaN, упомянутом в еще один ответ, а именно ((value < 0) == (value >= 0))
. Это оказалось хорошо работать с Visual C++, но не удалось с G++'S . Надежно работает только прямое тестирование bitpattern.
возможным решением, которое не зависело бы от конкретного представления IEEE для NaN, было бы следующее:
template<class T>
bool isnan( T f ) {
T _nan = (T)0.0/(T)0.0;
return 0 == memcmp( (void*)&f, (void*)&_nan, sizeof(T) );
}
Что касается меня, решение может быть макросом, чтобы сделать его явно встроенным и, следовательно, достаточно быстрым. Он также работает для любого типа float. Он основан на том факте, что единственный случай, когда значение не равно самому себе, - это когда значение не является числом.
#ifndef isnan
#define isnan(a) (a != a)
#endif
после прочтения других ответов я хотел что-то, что пройдет через предупреждение сравнения с плавающей запятой и не сломается при быстрой математике. Кажется, что работает следующий код:
/*
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;
}
учитывая это (x != x) не всегда гарантируется для NaN (например, при использовании опции-ffast-math), я использую:
#define IS_NAN(x) (((x) < 0) == ((x) >= 0))
числа не могут быть одновременно = 0, поэтому на самом деле эта проверка проходит только в том случае, если число не меньше, не больше или равно нулю. Что, по сути, вообще не число, или NaN.
вы также можете использовать это, если вы предпочитаете:
#define IS_NAN(x) (!((x)<0) && !((x)>=0)
Я не уверен, как это влияет на-ffast-math, поэтому ваш пробег может варьироваться.
это работает:
#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;
}
выход: isnan
Мне кажется, что лучшим действительно кросс-платформенным подходом было бы использовать объединение и проверить битовый шаблон double для проверки NaNs.
Я не полностью протестировал это решение, и может быть более эффективный способ работы с битовыми шаблонами, но я думаю, что он должен работать.
#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;
}
стандарт IEEE говорит когда экспонента все 1С и мантисса не равна нулю, номер Нэн. Double - это 1 бит знака, 11 бит экспоненты и 52 бита мантиссы. Проверь немного.
Как комментарии выше состояния a != a не будет работать в g++ и некоторых других компиляторах, но этот трюк должен. Это может быть не так эффективно, но это все еще способ:
bool IsNan(float a)
{
char s[4];
sprintf(s, "%.3f", a);
if (s[0]=='n') return true;
else return false;
}
в основном, в g++ (я не уверен в других) printf печатает " nan " на %d или %.F форматирует, если переменная не является допустимым целым числом / float. Поэтому этот код проверяет, что первый символ строки должен быть "n "(как в"nan")