ในฐานะของ 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 ในชื่อเดียวกัน แต่ในทางปฏิบัติไม่มีปัญหา ปัญหาหลักคือว่าเมื่อลอยจุดเพิ่มประสิทธิภาพทางคณิตศาสตร์มีการร้องขอแล้วกับคอมไพเลอร์หลักอย่างน้อยหนึ่งคือกรัม ++ std::isnan
ผลตอบแทนfalse
สำหรับอาร์กิวเมนต์น่าน
(fpclassify(value) == FP_NAN) )
ความทุกข์จากปัญหาเดียวกับที่std::isnan
กล่าวคือไม่น่าเชื่อถือ
(value != value) )
แนะนำในคำตอบ SO หลายข้อ ความทุกข์จากปัญหาเดียวกับที่std::isnan
กล่าวคือไม่น่าเชื่อถือ
(value == Fp_info::quiet_NaN()) )
นี่คือการทดสอบที่มีพฤติกรรมมาตรฐานไม่ควรตรวจพบ NaNs แต่ด้วยพฤติกรรมที่ได้รับการปรับให้เหมาะสมที่สุดอาจตรวจพบ NaNs (เนื่องจากรหัสที่ได้รับการปรับปรุงเพียงเปรียบเทียบการเป็นตัวแทนระดับบิตโดยตรง) และอาจรวมกับอีกวิธีหนึ่ง สามารถตรวจจับ NaN ได้อย่างน่าเชื่อถือ น่าเสียดายที่มันกลับกลายเป็นว่าไม่สามารถทำงานได้อย่างน่าเชื่อถือ
(ilogb(value) == FP_ILOGBNAN) )
ความทุกข์จากปัญหาเดียวกับที่std::isnan
กล่าวคือไม่น่าเชื่อถือ
isunordered(1.2345, value) )
ความทุกข์จากปัญหาเดียวกับที่std::isnan
กล่าวคือไม่น่าเชื่อถือ
is_ieee754_nan( value ) )
นี่ไม่ใช่ฟังก์ชั่นมาตรฐาน มันตรวจสอบบิตตามมาตรฐาน IEEE 754 มันน่าเชื่อถืออย่างสมบูรณ์แต่รหัสค่อนข้างขึ้นอยู่กับระบบ
ในรหัสการทดสอบที่สมบูรณ์ "ความสำเร็จ" ดังต่อไปนี้ไม่ว่าจะเป็นนิพจน์รายงานว่าไม่มีค่าของนิพจน์ สำหรับการแสดงออกส่วนใหญ่การวัดนี้ของความสำเร็จเป้าหมายของการตรวจสอบ NaNs และ NaNs เท่านั้นที่สอดคล้องกับความหมายมาตรฐานของพวกเขา อย่างไรก็ตามสำหรับการ(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 \ forum \ so \ 282 (ตรวจสอบ NaN)]
> g ++ --version | ค้นหา "++"
g ++ (x86_64-win32-sjlj-rev1 สร้างโดยโครงการ MinGW-W64) 6.3.0
[C: \ my \ forum \ so \ 282 (ตรวจสอบ NaN)]
> g ++ foo.cpp && a
คอมไพเลอร์อ้างสิทธิ์ IEEE 754 = จริง
v = nan, (std :: isnan (value)) = true สำเร็จ
u = 3.14, (std :: isnan (value)) = false สำเร็จ
w = inf, (std :: isnan (value)) = false สำเร็จ
v = nan, ((fpclassify (ค่า) == 0x0100)) = ความสำเร็จที่แท้จริง
u = 3.14, ((fpclassify (ค่า) == 0x0100)) = false สำเร็จ
w = inf, ((fpclassify (ค่า) == 0x0100)) = ความสำเร็จที่ผิดพลาด
v = nan, ((value! = value)) = true สำเร็จ
u = 3.14, ((ค่า! = ค่า)) = false สำเร็จ
w = inf, ((ค่า! = ค่า)) = false สำเร็จ
v = nan, ((ค่า == Fp_info :: quiet_NaN ())) = false ล้มเหลว
u = 3.14, ((ค่า == Fp_info :: quiet_NaN ())) = false สำเร็จ
w = inf, ((ค่า == Fp_info :: quiet_NaN ())) = false สำเร็จ
v = nan, ((ilogb (value) == ((int) 0x80000000))) = ความสำเร็จที่แท้จริง
u = 3.14, ((ilogb (ค่า) == ((int) 0x80000000))) = false สำเร็จ
w = inf, ((ilogb (ค่า) == ((int) 0x80000000))) = false สำเร็จ
v = nan, (isunordered (1.2345, ค่า)) = true Success
u = 3.14, (isunordered (1.2345, ค่า)) = false สำเร็จ
w = inf, (isunordered (1.2345, ค่า)) = false สำเร็จ
v = nan, (is_ieee754_nan (ค่า)) = true สำเร็จ
u = 3.14, (is_ieee754_nan (ค่า)) = false สำเร็จ
w = inf, (is_ieee754_nan (ค่า)) = false สำเร็จ
[C: \ my \ forum \ so \ 282 (ตรวจสอบ NaN)]
> g ++ foo.cpp -ffast-math && a
คอมไพเลอร์อ้างสิทธิ์ IEEE 754 = จริง
v = nan, (std :: isnan (ค่า)) = false FAILED
u = 3.14, (std :: isnan (value)) = false สำเร็จ
w = inf, (std :: isnan (value)) = false สำเร็จ
v = nan, ((fpclassify (ค่า) == 0x0100)) = false FAILED
u = 3.14, ((fpclassify (ค่า) == 0x0100)) = false สำเร็จ
w = inf, ((fpclassify (ค่า) == 0x0100)) = ความสำเร็จที่ผิดพลาด
v = nan, ((value! = value)) = false FAILED
u = 3.14, ((ค่า! = ค่า)) = false สำเร็จ
w = inf, ((ค่า! = ค่า)) = false สำเร็จ
v = nan, ((ค่า == Fp_info :: quiet_NaN ())) = ความสำเร็จที่แท้จริง
u = 3.14, ((ค่า == Fp_info :: quiet_NaN ())) = จริงล้มเหลว
w = inf, ((ค่า == Fp_info :: quiet_NaN ())) = true ล้มเหลว
v = nan, ((ilogb (value) == ((int) 0x80000000))) = ความสำเร็จที่แท้จริง
u = 3.14, ((ilogb (ค่า) == ((int) 0x80000000))) = false สำเร็จ
w = inf, ((ilogb (ค่า) == ((int) 0x80000000))) = false สำเร็จ
v = nan, (isunordered (1.2345, ค่า)) = false FAILED
u = 3.14, (isunordered (1.2345, ค่า)) = false สำเร็จ
w = inf, (isunordered (1.2345, ค่า)) = false สำเร็จ
v = nan, (is_ieee754_nan (ค่า)) = true สำเร็จ
u = 3.14, (is_ieee754_nan (ค่า)) = false สำเร็จ
w = inf, (is_ieee754_nan (ค่า)) = false สำเร็จ
[C: \ my \ forum \ so \ 282 (ตรวจสอบ NaN)]
> _
ผลลัพธ์ด้วย Visual C ++:
[C: \ my \ forum \ so \ 282 (ตรวจสอบ NaN)]
> cl / nologo- 2> & 1 | ค้นหา "++"
Microsoft (R) C / C ++ การปรับแต่งคอมไพเลอร์เวอร์ชั่น 19.00.23725 สำหรับ x86
[C: \ my \ forum \ so \ 282 (ตรวจสอบ NaN)]
> cl foo.cpp / Feb && b
foo.cpp
คอมไพเลอร์อ้างสิทธิ์ IEEE 754 = จริง
v = nan, (std :: isnan (value)) = true สำเร็จ
u = 3.14, (std :: isnan (value)) = false สำเร็จ
w = inf, (std :: isnan (value)) = false สำเร็จ
v = nan, ((fpclassify (value) == 2)) = ความสำเร็จที่แท้จริง
u = 3.14, ((fpclassify (ค่า) == 2)) = false สำเร็จ
w = inf, ((fpclassify (ค่า) == 2)) = false สำเร็จ
v = nan, ((value! = value)) = true สำเร็จ
u = 3.14, ((ค่า! = ค่า)) = false สำเร็จ
w = inf, ((ค่า! = ค่า)) = false สำเร็จ
v = nan, ((ค่า == Fp_info :: quiet_NaN ())) = false ล้มเหลว
u = 3.14, ((ค่า == Fp_info :: quiet_NaN ())) = false สำเร็จ
w = inf, ((ค่า == Fp_info :: quiet_NaN ())) = false สำเร็จ
v = nan, ((ilogb (value) == 0x7fffffff)) = ความสำเร็จที่แท้จริง
u = 3.14, ((ilogb (value) == 0x7fffffff)) = false สำเร็จ
w = inf, ((ilogb (ค่า) == 0x7fffffff)) = true ล้มเหลว
v = nan, (isunordered (1.2345, ค่า)) = true Success
u = 3.14, (isunordered (1.2345, ค่า)) = false สำเร็จ
w = inf, (isunordered (1.2345, ค่า)) = false สำเร็จ
v = nan, (is_ieee754_nan (ค่า)) = true สำเร็จ
u = 3.14, (is_ieee754_nan (ค่า)) = false สำเร็จ
w = inf, (is_ieee754_nan (ค่า)) = false สำเร็จ
[C: \ my \ forum \ so \ 282 (ตรวจสอบ NaN)]
> cl foo.cpp / Feb / fp: fast && b
foo.cpp
คอมไพเลอร์อ้างสิทธิ์ IEEE 754 = จริง
v = nan, (std :: isnan (value)) = true สำเร็จ
u = 3.14, (std :: isnan (value)) = false สำเร็จ
w = inf, (std :: isnan (value)) = false สำเร็จ
v = nan, ((fpclassify (value) == 2)) = ความสำเร็จที่แท้จริง
u = 3.14, ((fpclassify (ค่า) == 2)) = false สำเร็จ
w = inf, ((fpclassify (ค่า) == 2)) = false สำเร็จ
v = nan, ((value! = value)) = true สำเร็จ
u = 3.14, ((ค่า! = ค่า)) = false สำเร็จ
w = inf, ((ค่า! = ค่า)) = false สำเร็จ
v = nan, ((ค่า == Fp_info :: quiet_NaN ())) = false ล้มเหลว
u = 3.14, ((ค่า == Fp_info :: quiet_NaN ())) = false สำเร็จ
w = inf, ((ค่า == Fp_info :: quiet_NaN ())) = false สำเร็จ
v = nan, ((ilogb (value) == 0x7fffffff)) = ความสำเร็จที่แท้จริง
u = 3.14, ((ilogb (value) == 0x7fffffff)) = false สำเร็จ
w = inf, ((ilogb (ค่า) == 0x7fffffff)) = true ล้มเหลว
v = nan, (isunordered (1.2345, ค่า)) = true Success
u = 3.14, (isunordered (1.2345, ค่า)) = false สำเร็จ
w = inf, (isunordered (1.2345, ค่า)) = false สำเร็จ
v = nan, (is_ieee754_nan (ค่า)) = true สำเร็จ
u = 3.14, (is_ieee754_nan (ค่า)) = false สำเร็จ
w = inf, (is_ieee754_nan (ค่า)) = false สำเร็จ
[C: \ my \ forum \ so \ 282 (ตรวจสอบ NaN)]
> _
สรุปผลข้างต้นเพียงการทดสอบโดยตรงของการเป็นตัวแทนระดับบิตโดยใช้is_ieee754_nan
ฟังก์ชั่นที่กำหนดไว้ในโปรแกรมทดสอบนี้ทำงานได้อย่างน่าเชื่อถือในทุกกรณีที่มีทั้ง g ++ และ Visual C ++
ภาคผนวก:
หลังจากโพสต์ดังกล่าวข้างต้นที่ผมเริ่มตระหนักถึงความเป็นไปได้อีกในการทดสอบสำหรับน่านกล่าวถึงในคำตอบอื่น((value < 0) == (value >= 0))
ที่นี่คือ ที่เปิดใช้งานได้ดีกับ Visual C ++ แต่ล้มเหลวด้วย-ffast-math
ตัวเลือกของ g ++ การทดสอบบิตเทอร์เทิร์นโดยตรงเท่านั้นทำงานได้อย่างน่าเชื่อถือ