Начиная с C ++ 14 существует несколько способов проверить, является ли число с плавающей запятой value
NaN.
Из этих способов проверяется только проверка битов представления числа, работает надежно, как отмечено в моем первоначальном ответе. В частности, 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()) )
Это тест, который при стандартном поведении не должен определять NaN, но что с оптимизированным поведением возможно обнаружить NaNs ( из-за оптимизированного кода, непосредственно сравнивающего представления bitlevel) и, возможно, в сочетании с другим способом охвата стандартного не оптимизированного поведения, может надежно обнаружить NaN. К сожалению, оказалось, что он не работает надежно. (ilogb(value) == FP_ILOGBNAN) )
Страдает от той же проблемы, что и std::isnan
, т. Е. Ненадежна. isunordered(1.2345, value) )
Страдает от той же проблемы как std::isnan
, т. е. не является надежным. is_ieee754_nan( value ) )
Это не стандартная функция. Это проверка бит в соответствии со стандартом IEEE 754. Это полностью надежный , но код несколько зависит от системы. В следующем полном тестовом коде & ldquo; success & rdquo; является ли выражение сообщает об отсутствии значения. Для большинства выражений эта мера успеха, цель обнаружения NaNs и только NaNs, соответствует их стандартной семантике. Однако для выражения (value == Fp_info::quiet_NaN()) )
стандартное поведение заключается в том, что он не работает как NaN-детектор.
#include // std::isnan, std::fpclassify
#include
#include // std::setw
#include
#include // CHAR_BIT
#include
#include // 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;
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( 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)] > 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)] > _
Результаты с 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)] > _
Подведение итогов выше результатов, только прямое тестирование представления на уровне бит, используя функцию is_ieee754_nan
, определенную в этой тестовой программе, надежно работало во всех случаях как с g ++, так и с Visual C ++.
Addendum: After после публикации выше я узнал еще одну возможность проверить для NaN, упомянутую в еще один ответ здесь, а именно ((value < 0) == (value >= 0))
. Это оказалось хорошо работать с Visual C ++, но с ошибкой с опцией g ++ -ffast-math
. Только прямое тестирование битпатента работает надежно.