Начиная с 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. Это абсолютно надежно, но код в некоторой степени зависит от системы.
В следующем полном тестовом коде «успех» означает, сообщает ли выражение о значении значения. Для большинства выражений эта мера успеха, цель обнаружения 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 (обнаружение NaN)]
> g ++ - версия | найти "++"
g ++ (x86_64-win32-sjlj-rev1, построен проектом MinGW-W64) 6.3.0
[C: \ my \ forums \ so \ 282 (обнаружение NaN)]
> g ++ foo.cpp && a
Заявление компилятора IEEE 754 = true
v = nan, (std :: isnan (value)) = истинный успех
u = 3,14, (std :: isnan (значение)) = ложь Успех
w = inf, (std :: isnan (value)) = false Успех
v = nan, ((fpclassify (value) == 0x0100)) = true Успех
u = 3.14, ((fpclassify (value) == 0x0100)) = false Успех
w = inf, ((fpclassify (value) == 0x0100)) = false Успех
v = nan, ((значение! = значение)) = истинный успех
и = 3.14, ((значение! = значение)) = ложь Успех
w = inf, ((значение! = значение)) = ложь Успех
v = nan, ((значение == Fp_info :: quiet_NaN ())) = false FAILED
u = 3.14, ((значение == Fp_info :: quiet_NaN ())) = false Успешно
w = inf, ((значение == Fp_info :: quiet_NaN ())) = false Успех
v = nan, ((ilogb (значение) == ((int) 0x80000000))) = true Успех
u = 3.14, ((ilogb (значение) == ((int) 0x80000000))) = false Успех
w = inf, ((ilogb (значение) == ((int) 0x80000000))) = false Успех
v = nan, (isorordered (1.2345, value)) = истинный успех
U = 3,14, (неупорядочено (1,2345, значение)) = ложь Успех
w = inf, (isorordered (1.2345, value)) = false Успех
v = nan, (is_ieee754_nan (value)) = истинный успех
u = 3,14, (is_ieee754_nan (значение)) = ложь Успех
w = inf, (is_ieee754_nan (value)) = false Успех
[C: \ my \ forums \ so \ 282 (обнаружение NaN)]
> g ++ foo.cpp -ffast-math && a
Заявление компилятора IEEE 754 = true
v = nan, (std :: isnan (value)) = false FAILED
u = 3,14, (std :: isnan (значение)) = ложь Успех
w = inf, (std :: isnan (value)) = false Успех
v = nan, ((fpclassify (value) == 0x0100)) = false FAILED
u = 3.14, ((fpclassify (value) == 0x0100)) = false Успех
w = inf, ((fpclassify (value) == 0x0100)) = false Успех
v = nan, ((value! = value)) = false FAILED
и = 3.14, ((значение! = значение)) = ложь Успех
w = inf, ((значение! = значение)) = ложь Успех
v = nan, ((значение == Fp_info :: quiet_NaN ())) = истинный успех
u = 3.14, ((значение == Fp_info :: quiet_NaN ())) = true FAILED
w = inf, ((значение == Fp_info :: quiet_NaN ())) = true FAILED
v = nan, ((ilogb (значение) == ((int) 0x80000000))) = true Успех
u = 3.14, ((ilogb (значение) == ((int) 0x80000000))) = false Успех
w = inf, ((ilogb (значение) == ((int) 0x80000000))) = false Успех
v = nan, (isorordered (1.2345, value)) = false FAILED
U = 3,14, (неупорядочено (1,2345, значение)) = ложь Успех
w = inf, (isorordered (1.2345, value)) = false Успех
v = nan, (is_ieee754_nan (value)) = истинный успех
u = 3,14, (is_ieee754_nan (значение)) = ложь Успех
w = inf, (is_ieee754_nan (value)) = false Успех
[C: \ my \ forums \ so \ 282 (обнаружение NaN)]
> _
Результаты с Visual C ++:
[C: \ my \ forums \ so \ 282 (обнаружение NaN)]
> cl / nologo- 2> & 1 | найти "++"
Оптимизирующий компилятор Microsoft (R) C / C ++ версии 19.00.23725 для x86
[C: \ my \ forums \ so \ 282 (обнаружение NaN)]
> cl foo.cpp / Feb && b
foo.cpp
Заявление компилятора IEEE 754 = true
v = nan, (std :: isnan (value)) = истинный успех
u = 3,14, (std :: isnan (значение)) = ложь Успех
w = inf, (std :: isnan (value)) = false Успех
v = nan, ((fpclassify (value) == 2)) = true Успех
u = 3.14, ((fpclassify (value) == 2)) = false Успешно
w = inf, ((fpclassify (value) == 2)) = false Успешно
v = nan, ((значение! = значение)) = истинный успех
и = 3.14, ((значение! = значение)) = ложь Успех
w = inf, ((значение! = значение)) = ложь Успех
v = nan, ((значение == Fp_info :: quiet_NaN ())) = false FAILED
u = 3.14, ((значение == Fp_info :: quiet_NaN ())) = false Успешно
w = inf, ((значение == Fp_info :: quiet_NaN ())) = false Успех
v = nan, ((ilogb (значение) == 0x7fffffff)) = истинный успех
u = 3.14, ((ilogb (значение) == 0x7fffffff)) = false Успех
w = inf, ((ilogb (значение) == 0x7fffffff)) = true FAILED
v = nan, (isorordered (1.2345, value)) = истинный успех
U = 3,14, (неупорядочено (1,2345, значение)) = ложь Успех
w = inf, (isorordered (1.2345, value)) = false Успех
v = nan, (is_ieee754_nan (value)) = истинный успех
u = 3,14, (is_ieee754_nan (значение)) = ложь Успех
w = inf, (is_ieee754_nan (value)) = false Успех
[C: \ my \ forums \ so \ 282 (обнаружение NaN)]
> cl foo.cpp / Feb / fp: fast && b
foo.cpp
Заявление компилятора IEEE 754 = true
v = nan, (std :: isnan (value)) = истинный успех
u = 3,14, (std :: isnan (значение)) = ложь Успех
w = inf, (std :: isnan (value)) = false Успех
v = nan, ((fpclassify (value) == 2)) = true Успех
u = 3.14, ((fpclassify (value) == 2)) = false Успешно
w = inf, ((fpclassify (value) == 2)) = false Успешно
v = nan, ((значение! = значение)) = истинный успех
и = 3.14, ((значение! = значение)) = ложь Успех
w = inf, ((значение! = значение)) = ложь Успех
v = nan, ((значение == Fp_info :: quiet_NaN ())) = false FAILED
u = 3.14, ((значение == Fp_info :: quiet_NaN ())) = false Успешно
w = inf, ((значение == Fp_info :: quiet_NaN ())) = false Успех
v = nan, ((ilogb (значение) == 0x7fffffff)) = истинный успех
u = 3.14, ((ilogb (значение) == 0x7fffffff)) = false Успех
w = inf, ((ilogb (значение) == 0x7fffffff)) = true FAILED
v = nan, (isorordered (1.2345, value)) = истинный успех
U = 3,14, (неупорядочено (1,2345, значение)) = ложь Успех
w = inf, (isorordered (1.2345, value)) = false Успех
v = nan, (is_ieee754_nan (value)) = истинный успех
u = 3,14, (is_ieee754_nan (значение)) = ложь Успех
w = inf, (is_ieee754_nan (value)) = false Успех
[C: \ my \ forums \ so \ 282 (обнаружение NaN)]
> _
Подводя итог вышеприведенным результатам, только прямое тестирование представления на битовом уровне с использованием is_ieee754_nan
функции, определенной в этой тестовой программе, надежно работало во всех случаях как с g ++, так и с Visual C ++.
Приложение:
После публикации вышеизложенного я узнал о еще одном возможном тесте на NaN, упомянутом в другом ответе здесь, а именно ((value < 0) == (value >= 0))
. Это оказалось нормально работать с Visual C ++, но не с -ffast-math
опцией g ++ . Только прямое бит-тестирование работает надежно.