C ++ 14에서 부동 소수점 숫자 value
가 NaN 인지 테스트하는 방법에는 여러 가지가 있습니다.
이 방법들 중에서 , 숫자의 표현 비트 만 검사하는 것은 원래의 대답에서 언급했듯이 안정적으로 작동합니다. 특히, std::isnan
자주 제안되는 check v != v
, 누군가가 부동 소수점 최적화가 필요하다고 결정할 때 코드가 올바르게 작동하지 않도록 컴파일러가 안정적으로 작동하지 않아야하며 사용해서는 안됩니다. 이 상황은 바뀔 수 있고 컴파일러는 더 잘 적응할 수 있지만 원래의 대답 이후 6 년 동안 발생하지 않은이 문제에 대해.
약 6 년 동안 제 원래의 대답은이 질문에 대한 선택된 솔루션이었습니다. 그러나 최근 신뢰할 수없는 v != v
테스트를 추천하는 높은 평가를받은 답변 이 선택되었습니다. 따라서이 추가 최신 답변 (현재 C ++ 11 및 C ++ 14 표준과 C ++ 17이 있습니다).
C ++ 14에서 NaN-ness를 확인하는 주요 방법은 다음과 같습니다.
std::isnan(value) )
C ++ 11 이후로 의도 된 표준 라이브러리 방식입니다. isnan
분명히 같은 이름의 Posix 매크로와 충돌하지만 실제로는 문제가되지 않습니다. 주요 문제는 부동 소수점 산술 최적화가 요청 된 경우 하나 이상의 기본 컴파일러, 즉 g ++를 사용 하여 NaN 인수를 std::isnan
반환false
한다는 것 입니다.
(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()) )
표현하지만, 표준 동작은이 할머니 검출기로 작동하지 않습니다.
#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 \ 포럼 \ so \ 282 (NaN 감지)]
> g ++-버전 | "++"찾기
g ++ (x86_64-win32-sjlj-rev1, MinGW-W64 프로젝트에 의해 빌드 됨) 6.3.0
[C : \ my \ 포럼 \ so \ 282 (NaN 감지)]
> g ++ foo.cpp && a
컴파일러, IEEE 754 = 클레임
v = nan, (std :: isnan (value)) = true 성공
u = 3.14, (std :: isnan (value)) = 거짓 성공
w = inf, (std :: isnan (value)) = 거짓 성공
v = nan, ((fpclassify (value) == 0x0100)) = true 성공
u = 3.14, ((fpclassify (value) == 0x0100)) = 거짓 성공
w = inf, ((fpclassify (value) == 0x0100)) = 거짓 성공
v = nan, ((value! = value)) = true 성공
u = 3.14, ((value! = value)) = 거짓 성공
w = inf, ((value! = value)) = 거짓 성공
v = nan, ((value == Fp_info :: quiet_NaN ())) = 거짓 FAILED
u = 3.14, ((value == Fp_info :: quiet_NaN ())) = 거짓 성공
w = inf, ((value == Fp_info :: quiet_NaN ())) = 거짓 성공
v = nan, ((ilogb (value) == ((int) 0x80000000))) = 진정한 성공
u = 3.14, ((ilogb (value) == ((int) 0x80000000))) = 거짓 성공
w = inf, ((ilogb (value) == ((int) 0x80000000))) = 거짓 성공
v = nan, (isun order (1.2345, value)) = 진정한 성공
u = 3.14, (isun order (1.2345, value)) = 거짓 성공
w = inf, (isun order (1.2345, value)) = 거짓 성공
v = nan, (is_ieee754_nan (value)) = 진정한 성공
u = 3.14, (is_ieee754_nan (value)) = 거짓 성공
w = inf, (is_ieee754_nan (value)) = 거짓 성공
[C : \ my \ 포럼 \ so \ 282 (NaN 감지)]
> g ++ foo.cpp -ffast-math && a
컴파일러, IEEE 754 = 클레임
v = nan, (std :: isnan (value)) = 거짓 실패
u = 3.14, (std :: isnan (value)) = 거짓 성공
w = inf, (std :: isnan (value)) = 거짓 성공
v = nan, ((fpclassify (value) == 0x0100)) = 거짓 실패
u = 3.14, ((fpclassify (value) == 0x0100)) = 거짓 성공
w = inf, ((fpclassify (value) == 0x0100)) = 거짓 성공
v = nan, ((value! = value)) = 거짓 실패
u = 3.14, ((value! = value)) = 거짓 성공
w = inf, ((value! = value)) = 거짓 성공
v = nan, ((value == Fp_info :: quiet_NaN ())) = 진정한 성공
u = 3.14, ((값 == Fp_info :: quiet_NaN ())) = true 실패
w = inf, ((값 == Fp_info :: quiet_NaN ())) = true 실패
v = nan, ((ilogb (value) == ((int) 0x80000000))) = 진정한 성공
u = 3.14, ((ilogb (value) == ((int) 0x80000000))) = 거짓 성공
w = inf, ((ilogb (value) == ((int) 0x80000000))) = 거짓 성공
v = nan, (isun order (1.2345, value)) = 거짓 실패
u = 3.14, (isun order (1.2345, value)) = 거짓 성공
w = inf, (isun order (1.2345, value)) = 거짓 성공
v = nan, (is_ieee754_nan (value)) = 진정한 성공
u = 3.14, (is_ieee754_nan (value)) = 거짓 성공
w = inf, (is_ieee754_nan (value)) = 거짓 성공
[C : \ my \ 포럼 \ so \ 282 (NaN 감지)]
> _
Visual C ++를 사용한 결과 :
[C : \ my \ 포럼 \ so \ 282 (NaN 감지)]
> cl / nologo- 2> & 1 | "++"찾기
x86 용 Microsoft (R) C / C ++ 최적화 컴파일러 버전 19.00.23725
[C : \ my \ 포럼 \ so \ 282 (NaN 감지)]
> cl foo.cpp / 2 월 && b
foo.cpp
컴파일러, IEEE 754 = 클레임
v = nan, (std :: isnan (value)) = true 성공
u = 3.14, (std :: isnan (value)) = 거짓 성공
w = inf, (std :: isnan (value)) = 거짓 성공
v = nan, ((fpclassify (value) == 2)) = 진정한 성공
u = 3.14, ((fpclassify (value) == 2)) = 거짓 성공
w = inf, ((fpclassify (value) == 2)) = 거짓 성공
v = nan, ((value! = value)) = true 성공
u = 3.14, ((value! = value)) = 거짓 성공
w = inf, ((value! = value)) = 거짓 성공
v = nan, ((value == Fp_info :: quiet_NaN ())) = 거짓 FAILED
u = 3.14, ((value == Fp_info :: quiet_NaN ())) = 거짓 성공
w = inf, ((value == Fp_info :: quiet_NaN ())) = 거짓 성공
v = nan, ((ilogb (value) == 0x7fffffff)) = 진정한 성공
u = 3.14, ((ilogb (value) == 0x7fffffff)) = 거짓 성공
w = inf, ((ilogb (value) == 0x7fffffff)) = true 실패
v = nan, (isun order (1.2345, value)) = 진정한 성공
u = 3.14, (isun order (1.2345, value)) = 거짓 성공
w = inf, (isun order (1.2345, value)) = 거짓 성공
v = nan, (is_ieee754_nan (value)) = 진정한 성공
u = 3.14, (is_ieee754_nan (value)) = 거짓 성공
w = inf, (is_ieee754_nan (value)) = 거짓 성공
[C : \ my \ 포럼 \ so \ 282 (NaN 감지)]
> cl foo.cpp / Feb / fp : 빠른 && b
foo.cpp
컴파일러, IEEE 754 = 클레임
v = nan, (std :: isnan (value)) = true 성공
u = 3.14, (std :: isnan (value)) = 거짓 성공
w = inf, (std :: isnan (value)) = 거짓 성공
v = nan, ((fpclassify (value) == 2)) = 진정한 성공
u = 3.14, ((fpclassify (value) == 2)) = 거짓 성공
w = inf, ((fpclassify (value) == 2)) = 거짓 성공
v = nan, ((value! = value)) = true 성공
u = 3.14, ((value! = value)) = 거짓 성공
w = inf, ((value! = value)) = 거짓 성공
v = nan, ((value == Fp_info :: quiet_NaN ())) = 거짓 FAILED
u = 3.14, ((value == Fp_info :: quiet_NaN ())) = 거짓 성공
w = inf, ((value == Fp_info :: quiet_NaN ())) = 거짓 성공
v = nan, ((ilogb (value) == 0x7fffffff)) = 진정한 성공
u = 3.14, ((ilogb (value) == 0x7fffffff)) = 거짓 성공
w = inf, ((ilogb (value) == 0x7fffffff)) = true 실패
v = nan, (isun order (1.2345, value)) = 진정한 성공
u = 3.14, (isun order (1.2345, value)) = 거짓 성공
w = inf, (isun order (1.2345, value)) = 거짓 성공
v = nan, (is_ieee754_nan (value)) = 진정한 성공
u = 3.14, (is_ieee754_nan (value)) = 거짓 성공
w = inf, (is_ieee754_nan (value)) = 거짓 성공
[C : \ my \ 포럼 \ so \ 282 (NaN 감지)]
> _
위의 결과를 종합하면 is_ieee754_nan
이 테스트 프로그램에 정의 된 함수를 사용하여 비트 수준 표현의 직접 테스트 만 g ++ 및 Visual C ++ 모두에서 안정적으로 작동했습니다.
부록 :
위의 내용을 게시 한 후 여기 에 또 다른 답변으로 언급 된 NaN을 테스트 할 수있는 또 다른 가능성이 있음을 알게되었습니다 ((value < 0) == (value >= 0))
. Visual C ++에서는 잘 작동하지만 g ++의 -ffast-math
옵션으로는 실패했습니다 . 직접 비트 패턴 테스트 만 안정적으로 작동합니다.