Начиная с C ++ 14, есть несколько способов проверить, является ли число с плавающей запятой value
NaN.
Из этих способов только проверка битов представления числа,
работает надежно, как отмечено в моем первоначальном ответе. В частности, std::isnan
и часто предлагаемая проверка v != v
не работают надежно и не должны использоваться, иначе ваш код перестанет работать правильно, когда кто-то решит, что необходима оптимизация с плавающей запятой, и попросит компилятор сделать это. Эта ситуация может измениться, компиляторы могут стать более соответствующими, но для этой проблемы, которая не возникала в течение 6 лет с момента первоначального ответа.
Около 6 лет моим первоначальным ответом было выбранное решение для этого вопроса, которое было в порядке. Но недавно был выбран ответ с сильным голосом, рекомендующий ненадежный тест v != v
. Отсюда и этот дополнительный более актуальный ответ (теперь у нас есть стандарты C ++ 11 и C ++ 14 и C ++ 17 на горизонте).
Основные способы проверки NaN-сущности, начиная с 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()) )
Это тест, который при стандартном поведении не должен обнаруживать NaN, но при
оптимизированное поведение, возможно, может обнаружить NaN (из-за оптимизированного кода, просто сравнивая
представления на уровне битов непосредственно), и, возможно, в сочетании с другим способом
охватывают стандартное неоптимизированное поведение, может надежно определять NaN к несчастью
оказалось, что не работает надежно.
(ilogb(value) == FP_ILOGBNAN) )
Страдает от той же проблемы, что и std::isnan
, то есть не надежна.
isunordered(1.2345, value) )
Страдает от той же проблемы, что и std::isnan
, то есть не надежна.
is_ieee754_nan( value ) )
Это не стандартная функция. Это проверка битов в соответствии с IEEE 754
стандарт. Это полностью надежный , но код в некоторой степени зависит от системы.
В следующем полном тестовом коде & ldquo; success & rdquo; является ли выражение сообщает Nanness значения. Для большинства выражений эта мера успеха, цель обнаружения NaN и только NaN, соответствует их стандартной семантике. Однако для выражения (value == Fp_info::quiet_NaN()) )
стандартное поведение состоит в том, что оно не работает как 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\so\282 (detect NaN)]
> <b><i>g++ --version | find "++"</i></b>
g++ (x86_64-win32-sjlj-rev1, Built by MinGW-W64 project) 6.3.0
[C:\my\forums\so\282 (detect NaN)]
> <b><i>g++ foo.cpp && a</i></b>
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)]
> <b><i>g++ foo.cpp -ffast-math && a</i></b>
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)]
> _
Результаты с Visual C ++:
[C:\my\forums\so\282 (detect NaN)]
> <b><i>cl /nologo- 2>&1 | find "++"</i></b>
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23725 for x86
[C:\my\forums\so\282 (detect NaN)]
> <b><i>cl foo.cpp /Feb && b</i></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)]
> <b><i>cl foo.cpp /Feb /fp:fast && b</i></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)]
> _
Суммируя вышеприведенные результаты, только прямое тестирование представления на битовом уровне с использованием функции is_ieee754_nan
, определенной в этой тестовой программе, надежно работало во всех случаях как с g ++, так и с Visual C ++.
Добавление:
После публикации вышеизложенного мне стало известно о еще одном возможном тесте на NaN, упомянутом в другом ответе здесь, а именно ((value < 0) == (value >= 0))
. Это оказалось хорошо работать с Visual C ++, но не удалось с опцией g ++ -ffast-math
. Надежно работает только прямое тестирование на наличие бит-паттернов.