C ++ 14以降、浮動小数点数value
がNaN かどうかをテストする方法はいくつかあります。
これらの方法のうち、私の元の回答で述べたように、数値表現のビットのチェックのみが確実に機能します。特に、std::isnan
頻繁に提案されるチェックv != v
は確実に機能しないため、使用しないでください。浮動小数点の最適化が必要であると誰かが判断してコンパイラーに実行を依頼したときにコードが正しく機能しないようにしてください。この状況は変化する可能性があり、コンパイラーはより準拠する可能性がありますが、この問題については、最初の回答から6年間は発生していません。
約6年間、私の最初の答えはこの質問の選択された解決策でしたが、問題ありませんでした。しかし最近、信頼性の低いv != v
テストを推奨する非常に支持された回答が選択されました。したがって、この追加の最新の回答(C ++ 11とC ++ 14の標準、およびC ++ 17が間もなく登場します)。
C ++ 14以降のNaN性をチェックする主な方法は次のとおりです。
std::isnan(value) )
C ++ 11以降の標準ライブラリの方法です。isnan
明らかに同じ名前のPosixマクロと競合しますが、実際にはそれは問題ではありません。主な問題は、浮動小数点算術最適化が要求されたときに、少なくとも1つのメインコンパイラ、つまり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 ++ --version | 「++」を見つける
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))= true成功
u = 3.14、(std :: isnan(value))= false成功
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、((value!= value))= true成功
u = 3.14、((value!= value))= false成功
w = inf、((value!= value))= false成功
v = nan、((value == Fp_info :: quiet_NaN()))= false失敗
u = 3.14、((value == Fp_info :: quiet_NaN()))= false成功
w = inf、((value == Fp_info :: quiet_NaN()))= false成功
v = nan、((ilogb(value)==((int)0x80000000)))= true成功
u = 3.14、((ilogb(value)==((int)0x80000000)))= false成功
w = inf、((ilogb(value)==((int)0x80000000)))= false成功
v = nan、(isunordered(1.2345、value))= true成功
u = 3.14、(isunordered(1.2345、value))= false成功
w = inf、(isunordered(1.2345、value))= false成功
v = nan、(is_ieee754_nan(value))= true成功
u = 3.14、(is_ieee754_nan(value))= false成功
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失敗
u = 3.14、(std :: isnan(value))= false成功
w = inf、(std :: isnan(value))= false成功
v = nan、((fpclassify(value)== 0x0100))= false失敗
u = 3.14、((fpclassify(value)== 0x0100))= false成功
w = inf、((fpclassify(value)== 0x0100))= false成功
v = nan、((value!= value))= false失敗
u = 3.14、((value!= value))= false成功
w = inf、((value!= value))= false成功
v = nan、((value == Fp_info :: quiet_NaN()))= true成功
u = 3.14、((value == Fp_info :: quiet_NaN()))= true失敗
w = inf、((value == Fp_info :: quiet_NaN()))= true失敗
v = nan、((ilogb(value)==((int)0x80000000)))= true成功
u = 3.14、((ilogb(value)==((int)0x80000000)))= false成功
w = inf、((ilogb(value)==((int)0x80000000)))= false成功
v = nan、(isunordered(1.2345、value))= false失敗
u = 3.14、(isunordered(1.2345、value))= false成功
w = inf、(isunordered(1.2345、value))= false成功
v = nan、(is_ieee754_nan(value))= true成功
u = 3.14、(is_ieee754_nan(value))= false成功
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 ++ Optimizing Compilerバージョン19.00.23725 for x86
[C:\ my \ forums \ so \ 282(NaNを検出)]
> cl foo.cpp / Feb && b
foo.cpp
コンパイラはIEEE 754 = trueと主張している
v = nan、(std :: isnan(value))= true成功
u = 3.14、(std :: isnan(value))= false成功
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、((value!= value))= true成功
u = 3.14、((value!= value))= false成功
w = inf、((value!= value))= false成功
v = nan、((value == Fp_info :: quiet_NaN()))= false失敗
u = 3.14、((value == Fp_info :: quiet_NaN()))= false成功
w = inf、((value == Fp_info :: quiet_NaN()))= false成功
v = nan、((ilogb(value)== 0x7fffffff))= true成功
u = 3.14、((ilogb(value)== 0x7fffffff))= false成功
w = inf、((ilogb(value)== 0x7fffffff))= true失敗
v = nan、(isunordered(1.2345、value))= true成功
u = 3.14、(isunordered(1.2345、value))= false成功
w = inf、(isunordered(1.2345、value))= false成功
v = nan、(is_ieee754_nan(value))= true成功
u = 3.14、(is_ieee754_nan(value))= false成功
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))= true成功
u = 3.14、(std :: isnan(value))= false成功
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、((value!= value))= true成功
u = 3.14、((value!= value))= false成功
w = inf、((value!= value))= false成功
v = nan、((value == Fp_info :: quiet_NaN()))= false失敗
u = 3.14、((value == Fp_info :: quiet_NaN()))= false成功
w = inf、((value == Fp_info :: quiet_NaN()))= false成功
v = nan、((ilogb(value)== 0x7fffffff))= true成功
u = 3.14、((ilogb(value)== 0x7fffffff))= false成功
w = inf、((ilogb(value)== 0x7fffffff))= true失敗
v = nan、(isunordered(1.2345、value))= true成功
u = 3.14、(isunordered(1.2345、value))= false成功
w = inf、(isunordered(1.2345、value))= false成功
v = nan、(is_ieee754_nan(value))= true成功
u = 3.14、(is_ieee754_nan(value))= false成功
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 ++ではうまく機能することが判明しましたが、g ++の-ffast-math
オプションでは失敗しました。直接ビットパターンテストのみが確実に機能します。