Kiểm tra nếu một đôi (hoặc float) là NaN trong C ++


368

Có một hàm isnan () không?

Tái bút: Tôi đang ở MinGW (nếu điều đó tạo ra sự khác biệt).

Tôi đã giải quyết vấn đề này bằng cách sử dụng isnan () từ <math.h>, thứ không tồn tại <cmath>, #includelúc đầu tôi đang sử dụng .


2
Tôi không thuần túy bạn có thể làm điều đó một cách hợp lý. Ai nói C ++ yêu cầu IEEE754?
David Heffernan


Chỉ cần một lưu ý, 1 oz phòng ngừa tốt hơn 1 lb chữa bệnh. Nói cách khác, việc ngăn chặn 0.f / 0.f không bao giờ được thực thi sẽ tốt hơn nhiều so với việc kiểm tra hồi tố cho nanmã của bạn. nancó thể phá hủy khủng khiếp đối với chương trình của bạn, nếu được phép phổ biến nó có thể gây ra lỗi khó tìm. Điều này là do nanđộc hại, (5 * nan= nan), nankhông bằng bất cứ thứ gì ( nan! = nan), nanKhông lớn hơn bất cứ thứ gì ( nan!> 0), nankhông nhỏ hơn bất cứ thứ gì ( nan! <0).
bobobobo

1
@bobobobo: Đó là một tính năng, cho phép kiểm tra lỗi tập trung. Cũng giống như ngoại lệ so với giá trị trả về.
Ben Voigt

2
Tại sao <cmath> không có isnan ()? Đó là trong std ::
frankliuao

Câu trả lời:


349

Theo tiêu chuẩn của IEEE, các giá trị NaN có thuộc tính lẻ so sánh các giá trị liên quan đến chúng luôn luôn sai. Nghĩa là, đối với float f, f != fsẽ chỉ đúng nếu f là NaN.

Lưu ý rằng, như một số ý kiến ​​dưới đây đã chỉ ra, không phải tất cả các trình biên dịch đều tôn trọng điều này khi tối ưu hóa mã.

Đối với bất kỳ trình biên dịch nào tuyên bố sử dụng dấu phẩy động của IEEE, thủ thuật này sẽ hoạt động. Nhưng tôi không thể đảm bảo rằng nó sẽ hoạt động trong thực tế. Kiểm tra với trình biên dịch của bạn, nếu nghi ngờ.


4
Trình biên dịch tốt hơn không loại bỏ điều này nếu chạy trong chế độ IEEE. Kiểm tra tài liệu cho trình biên dịch của bạn, tất nhiên ...
dmckee --- ex-moderator mèo con

38
-1 chỉ hoạt động trên lý thuyết, không phải trong thực tế: các trình biên dịch như g ++ (với -fastmath) vít lên. cách chung duy nhất, cho đến c ++ 0x, là kiểm tra bitpotype.
Chúc mừng và hth. - Alf

66
@Alf: Tài liệu cho -ffast-mathtùy chọn nói rõ ràng rằng nó có thể dẫn đến đầu ra không chính xác cho các chương trình phụ thuộc vào việc triển khai chính xác nếu các quy tắc / thông số kỹ thuật của IEEE hoặc ISO cho các hàm toán học. Nếu không có tùy chọn đó được bật, sử dụng x != xlà cách kiểm tra NaN hoàn toàn hợp lệ và di động.
Adam Rosenfield

7
@Adam: tài liệu nói rõ rằng nó không phù hợp, vâng. và vâng, tôi đã gặp phải cuộc tranh luận đó trước đây, thảo luận về vấn đề này với Gabriel Dos Reis. nó thường được sử dụng để bảo vệ thiết kế, trong một cuộc tranh cãi vòng tròn (tôi không biết liệu bạn có ý định liên kết với điều đó không, nhưng đáng để biết - đó là công cụ chiến tranh rực lửa). kết luận của bạn x != xlà hợp lệ mà không có tùy chọn đó không tuân theo logic. nó có thể đúng với một phiên bản cụ thể của g ++ hoặc không. dù sao, bạn thường không có cách nào để đảm bảo rằng tùy chọn fastmath sẽ không được sử dụng.
Chúc mừng và hth. - Alf

7
@Alf: Không, tôi không biết về cuộc thảo luận của bạn với Gabriel Dos Reis. Steve Jessop đã đưa ra một điểm tuyệt vời trong câu hỏi khác về việc giả định đại diện của IEEE. Nếu bạn giả sử IEEE 754 và trình biên dịch đang hoạt động theo cách phù hợp (nghĩa là không có -ffast-mathtùy chọn), thì đó x != xlà một giải pháp hợp lệ và di động. Bạn thậm chí có thể kiểm tra -ffast-mathbằng cách kiểm tra __FAST_MATH__macro và chuyển sang triển khai khác trong trường hợp đó (ví dụ: sử dụng các hiệp nhất và xoay vòng bit).
Adam Rosenfield

220

Không có isnan()chức năng có sẵn trong Thư viện chuẩn C ++ hiện tại. Nó được giới thiệu trong C99 và được định nghĩa là một macro không phải là hàm. Các yếu tố của thư viện tiêu chuẩn được xác định bởi C99 không phải là một phần của tiêu chuẩn C ++ hiện tại ISO / IEC 14882: 1998 cũng không phải là bản cập nhật ISO / IEC 14882: 2003.

Năm 2005 Báo cáo kỹ thuật 1 đã được đề xuất. TR1 mang lại khả năng tương thích với C99 với C ++. Mặc dù thực tế chưa bao giờ được chính thức áp dụng để trở thành tiêu chuẩn C ++, nhưng nhiều triển khai ( GCC 4.0+ hoặc Visual C ++ 9.0+ C ++ cung cấp các tính năng TR1, tất cả chúng hoặc chỉ một số (Visual C ++ 9.0 không cung cấp các hàm toán học C99) .

Nếu TR1 khả dụng, sau đó cmathbao gồm các phần tử C99 như isnan(), isfinite()v.v. nhưng chúng được xác định là hàm, không phải là macro, thường là trong std::tr1::không gian tên, mặc dù nhiều triển khai (ví dụ GCC 4+ trên Linux hoặc XCode trên Mac OS X 10.5+) trực tiếp std::, vì vậy std::isnanđược xác định rõ.

Hơn nữa, một số triển khai C ++ vẫn isnan()cung cấp macro C99 cho C ++ (bao gồm thông qua cmathhoặc math.h), điều có thể gây ra nhiều nhầm lẫn hơn và các nhà phát triển có thể cho rằng đó là hành vi tiêu chuẩn.

Một lưu ý về Viusal C ++, như đã đề cập ở trên, nó không cung cấp std::isnancả std::tr1::isnan, nhưng nó cung cấp một hàm mở rộng được xác định như _isnan()đã có sẵn từ đó Visual C ++ 6.0

Trên XCode, thậm chí còn vui hơn. Như đã đề cập, GCC 4+ định nghĩa std::isnan. Đối với các phiên bản cũ hơn của trình biên dịch và thư viện mẫu XCode, dường như (đây là cuộc thảo luận có liên quan ), chưa có cơ hội tự kiểm tra) hai chức năng được xác định, __inline_isnand()trên Intel và __isnand()trên Power PC.


21
Mọi người đều muốn các chức năng này như isNan hoặc isInfinity. Tại sao những người phụ trách không chỉ đơn giản bao gồm trong tiêu chuẩn của họ ???? - Tôi sẽ cố gắng tìm ra cách để chịu trách nhiệm và bỏ phiếu cho việc này. Nghiêm túc.
shuhalo

8
@shuhalo Phụ trách chưa?
Tomáš Zato - Tái lập lại

11
Câu trả lời này cần được cập nhật vì std::isnanhiện là một phần của tiêu chuẩn C ++ 11 và sự hỗ trợ đã lan rộng ra. std :: isnan đã được triển khai trong Visual Studio bắt đầu với Visual Studio 2013. Có lẽ @shuhalo đã chịu trách nhiệm :-)
aberaud

170

Giải pháp đầu tiên: nếu bạn đang sử dụng C ++ 11

Vì điều này đã được hỏi nên có một chút phát triển mới: điều quan trọng cần biết đó std::isnan()là một phần của C ++ 11

Tóm tắc

Được xác định trong tiêu đề <cmath>

bool isnan( float arg ); (since C++11)
bool isnan( double arg ); (since C++11)
bool isnan( long double arg ); (since C++11)

Xác định nếu arg số dấu phẩy động đã cho không phải là số ( NaN).

Thông số

arg: giá trị dấu phẩy động

Giá trị trả về

truenếu arg là NaN, falsenếu không

Tài liệu tham khảo

http://en.cppreference.com/w/cpp/numeric/math/is Nam

Xin lưu ý rằng điều này không tương thích với -fast-math nếu bạn sử dụng g ++, xem bên dưới để biết các đề xuất khác.


Các giải pháp khác: nếu bạn sử dụng các công cụ không tuân thủ C ++ 11

Đối với C99, trong C, điều này được triển khai dưới dạng macro isnan(c)trả về giá trị int. Các loại xsẽ được nổi, gấp đôi hoặc dài gấp đôi.

Các nhà cung cấp khác nhau có thể bao gồm hoặc không bao gồm hoặc không có chức năng isnan().

Cách được cho là di động để kiểm tra NaNlà sử dụng thuộc tính IEEE 754 NaNkhông bằng chính nó: tức là x == xsẽ sai xkhi tồn tại NaN.

Tuy nhiên, tùy chọn cuối cùng có thể không hoạt động với mọi trình biên dịch và một số cài đặt (đặc biệt là cài đặt tối ưu hóa), vì vậy, trong lần cuối cùng, bạn luôn có thể kiểm tra mẫu bit ...


8
Chắc chắn xứng đáng là câu trả lời được chấp nhận và xứng đáng nhận được nhiều sự ủng hộ hơn. Cảm ơn vì tiền boa
LBes

3
−1 std::isnan vẫn là một đề xuất không tốt kể từ tháng 2 năm 2017, vì nó không hoạt động với tối ưu hóa điểm nổi của g ++.
Chúc mừng và hth. - Alf

@ Cheersandhth.-Alf: tùy chọn này có tuân thủ chuẩn IEEE không? Câu trả lời đã được chỉnh sửa
BlueTrin

@BlueTrin: Cả hai x != xisnanđược yêu cầu phải hoạt động để tuân thủ IEEE 754. Về sau này, tiêu chuẩn IEEE 754-2008 tuyên bố rằng các Triển khai của Cung cấp sẽ cung cấp các hoạt động phi tính toán sau đây cho tất cả các định dạng số học được hỗ trợ. Và và is is is is is if if if Để kiểm tra sự phù hợp mà tiêu chuẩn yêu cầu is754version1985()is754version2008(), trong đó C ++ thay vào đó cung cấp std::numeric_limits<Fp>::is_iec559()(IEC 559 là cùng tiêu chuẩn). Thật không may với -ffast-mathtối ưu hóa, ví dụ g ++ yêu cầu tuân thủ nhưng không tuân thủ.
Chúc mừng và hth. - Alf

1
Cảnh báo: isnan (x) không hoạt động với tùy chọn -ffinite-math-only in gcc and clang
A Fog

82

Ngoài ra còn có một thư viện chỉ có tiêu đề hiện diện trong Boost có các công cụ gọn gàng để đối phó với các kiểu dữ liệu dấu phẩy động

#include <boost/math/special_functions/fpclassify.hpp>

Bạn nhận được các chức năng sau:

template <class T> bool isfinite(T z);
template <class T> bool isinf(T t);
template <class T> bool isnan(T t);
template <class T> bool isnormal(T t);

Nếu bạn có thời gian thì hãy xem toàn bộ bộ công cụ Math từ Boost, nó có nhiều công cụ hữu ích và đang phát triển nhanh chóng.

Ngoài ra, khi xử lý các điểm nổi và không nổi, có thể nên xem qua Chuyển đổi số .


1
Cảm ơn! Chỉ cần những gì tôi đang tìm kiếm.
Tiến sĩ Watson

nó đã được thêm vào Boost 1.35 (Tôi vừa thấy chương trình của mình không biên dịch trên bản phân phối linux cũ).
marcin

2
nếu bạn biên dịch với tùy chọn --fast-math thì hàm này sẽ không hoạt động như mong đợi.
Gaetano Mendola

43

Có ba cách "chính thức": isnanmacro posix , isnanmẫu hàm c ++ 0x hoặc hàm c ++ trực quan_isnan .

Thật không may, thật không thực tế khi phát hiện ra cái nào sẽ sử dụng.

Thật không may, không có cách nào đáng tin cậy để phát hiện xem bạn có đại diện của IEEE 754 với NaN hay không. Thư viện tiêu chuẩn cung cấp một cách chính thức như vậy (numeric_limits<double>::is_iec559 ). Nhưng trong thực tế trình biên dịch như g ++ vít mà lên.

Về lý thuyết, người ta có thể sử dụng một cách đơn giản x != x, nhưng các trình biên dịch như g ++ và visual c ++ bắt vít.

Vì vậy, cuối cùng, hãy kiểm tra các bitp NaN cụ thể , giả sử (và hy vọng có thể thực thi, tại một số điểm!) Một đại diện cụ thể như IEEE 754.


EDIT : như một ví dụ về "trình biên dịch như g ++ vít vít lên", hãy xem xét

#include <limits>
#include <assert.h>

void foo( double a, double b )
{
    assert( a != b );
}

int main()
{
    typedef std::numeric_limits<double> Info;
    double const nan1 = Info::quiet_NaN();
    double const nan2 = Info::quiet_NaN();
    foo( nan1, nan2 );
}

Biên dịch với g ++ (TDM-2 mingw32) 4.4.1:

C: \ test> gõ "C: \ Chương trình tập tin \ @commands \ gnuc.bat"
@rem -finput-charset = windows-1252
@ g ++ -O -pedantic -std = c ++ 98 -Wall -Wwrite-chuỗi% * -Wno-long-long

C: \ test> gnuc x.cpp

C: \ test> a && echo hoạt động ... || echo! thất bại
làm...

C: \ test> gnuc x.cpp --fast-math

C: \ test> a && echo hoạt động ... || echo! thất bại
Xác nhận thất bại: a! = B, tệp x.cpp, dòng 6

Ứng dụng này đã yêu cầu Runtime chấm dứt nó theo một cách khác thường.
Vui lòng liên hệ với nhóm hỗ trợ của ứng dụng để biết thêm thông tin.
!thất bại

C: \ kiểm tra> _

4
@Alf: Ví dụ của bạn hoạt động như mong đợi đối với tôi trên cả Mac OS X và Linux trên các phiên bản khác nhau của g ++ trong khoảng từ 4.0 đến 4.5. Tài liệu cho -ffast-mathtùy chọn nói rõ ràng rằng nó có thể dẫn đến đầu ra không chính xác cho các chương trình phụ thuộc vào việc triển khai chính xác nếu các quy tắc / thông số kỹ thuật của IEEE hoặc ISO cho các hàm toán học. Nếu không có tùy chọn đó được bật, sử dụng x != xlà cách kiểm tra NaN hoàn toàn hợp lệ và di động.
Adam Rosenfield

6
@Adam: Điều bạn thiếu là tiêu chuẩn C ++ không yêu cầu biểu diễn hoặc toán học của IEEE cho phao. Theo như trang man nói với bạn, gcc -ffast-mathvẫn là một triển khai C ++ phù hợp (vâng, giả sử nó numeric_limits::is_iec559đúng, mặc dù Alf gợi ý ở trên là không): Mã C ++ dựa trên IEEE không phải là C ++ di động và không có quyền mong đợi việc thực hiện để cung cấp nó.
Steve Jessop

5
Và Alf đúng, thử nghiệm nhanh trên gcc 4.3.4 và is_iec559đúng với -ffast-math. Vì vậy, vấn đề ở đây là các tài liệu của GCC -ffast-mathchỉ nói rằng nó không phải là IEEE / ISO cho các hàm toán học, trong khi họ nên nói rằng đó không phải là C ++, bởi vì việc triển khai của numeric_limitsnó bị sai lệch. Tôi đoán rằng GCC không thể luôn luôn nói tại thời điểm mẫu được xác định, liệu phần phụ trợ cuối cùng có thực sự có phao phù hợp hay không, và do đó thậm chí không thử. IIRC có các vấn đề tương tự trong danh sách lỗi nổi bật về sự phù hợp C99 của GCC.
Steve Jessop

1
@Alf, @Steve, tôi không biết tiêu chuẩn C ++ không có thông số kỹ thuật về các giá trị dấu phẩy động. Nó khá sốc đối với tôi. Có vẻ như xử lý tốt hơn IEEE 754 và NaN như một phần mở rộng cụ thể của nền tảng thay vì tiêu chuẩn. Phải không? Và tôi có thể mong đợi bất kỳ loại isnan () hoặc IEEE754 nào được thêm vào trong C ++ 0x không?
Eonil

3
@Eonil: C ++ 0x vẫn có ví dụ "Biểu diễn giá trị của các loại điểm oating là triển khai thực hiện". Cả C và C ++ đều nhằm mục đích hỗ trợ triển khai trên các máy không có phần cứng dấu phẩy động và phao chuẩn IEEE 754 thích hợp có thể chậm hơn một chút để mô phỏng so với các lựa chọn thay thế chính xác hợp lý. Lý thuyết là bạn có thể khẳng định is_iec559nếu bạn cần IEEE, trong thực tế, điều đó dường như không hoạt động trên GCC. C ++ 0x không có isnanchức năng, nhưng vì GCC hiện không thực hiện đúng is_iec559, tôi đoán nó cũng không có trong C ++ 0x, và -ffast-mathcũng có thể phá vỡ nó isnan.
Steve Jessop

39

Có một std :: isnan nếu trình biên dịch của bạn hỗ trợ các phần mở rộng c99, nhưng tôi không chắc liệu mingw có làm không.

Đây là một chức năng nhỏ sẽ hoạt động nếu trình biên dịch của bạn không có chức năng tiêu chuẩn:

bool custom_isnan(double var)
{
    volatile double d = var;
    return d != d;
}

6
Tại sao không chỉ var! = var?
Brian R. Bondy

8
Khi làm điều đó là một cơ hội, trình biên dịch sẽ tối ưu hóa việc so sánh, luôn luôn trả về đúng.
CTT

23
Không, không có. Một trình biên dịch làm điều đó bị hỏng. Bạn cũng có thể nói rằng có khả năng thư viện chuẩn isnantrả về kết quả sai. Về mặt kỹ thuật, trình biên dịch có thể có lỗi, nhưng trên thực tế, Không phải Gonna Happen. Tương tự như var != var. Nó hoạt động vì đó là cách xác định các giá trị dấu phẩy động của IEEE.
jalf

29
if -ffast-math được đặt, isnan () sẽ không trả về kết quả đúng cho gcc. Tất nhiên, sự tối ưu hóa này được ghi nhận là phá vỡ ngữ nghĩa của IEEE ...
Matthew Herrmann

Nếu -ffast-math được đặt, thì trình biên dịch bị lỗi. Hay đúng hơn, nếu -ffast-math được đặt, tất cả các cược đã tắt và dù sao bạn cũng không thể dựa vào NaN.
Adrian Ratnapala 21/07/2015

25

Bạn có thể sử dụng numeric_limits<float>::quiet_NaN( )được xác định trong limitsthư viện tiêu chuẩn để kiểm tra. Có một hằng số riêng được định nghĩa cho double.

#include <iostream>
#include <math.h>
#include <limits>

using namespace std;

int main( )
{
   cout << "The quiet NaN for type float is:  "
        << numeric_limits<float>::quiet_NaN( )
        << endl;

   float f_nan = numeric_limits<float>::quiet_NaN();

   if( isnan(f_nan) )
   {
       cout << "Float was Not a Number: " << f_nan << endl;
   }

   return 0;
}

Tôi không biết nếu điều này hoạt động trên tất cả các nền tảng, vì tôi chỉ thử nghiệm với g ++ trên Linux.


2
Mặc dù vậy, hãy coi chừng - dường như có một lỗi trong num_limits trong phiên bản GCC 3.2.3, vì nó trả về 0,0 cho quiet_NaN. Các phiên bản sau của GCC đều ổn theo kinh nghiệm của tôi.
Nhà bếp của Nathan

@Nathan: Thật tốt khi biết. Tôi đang sử dụng phiên bản 4.3.2, vì vậy tôi ra khỏi rừng.
Bill Lizard

18

Bạn có thể sử dụng isnan()hàm, nhưng bạn cần bao gồm thư viện toán C.

#include <cmath>

Vì chức năng này là một phần của C99, nó không có sẵn ở mọi nơi. Nếu nhà cung cấp của bạn không cung cấp chức năng, bạn cũng có thể xác định biến thể của riêng mình để tương thích.

inline bool isnan(double x) {
    return x != x;
}

Tôi đã sử dụng <cmath> và không có isnan trong đó! tình cờ tôi phát hiện ra rằng có một isnantrong <math.h>
Hasen

1
Như tôi đã nói, đây là một phần của C99. Vì C99 không phải là một phần của bất kỳ tiêu chuẩn C ++ hiện tại nào, tôi đã cung cấp giải pháp thay thế. Nhưng vì có khả năng isnan () sẽ được đưa vào một tiêu chuẩn C ++ sắp tới, tôi đã đưa ra một chỉ thị #ifndef xung quanh nó.
raimue

12

Đoạn mã sau sử dụng định nghĩa của NAN (tất cả các bit lũy thừa, ít nhất một tập bit phân đoạn) và giả sử rằng sizeof (int) = sizeof (float) = 4. Bạn có thể tra cứu NAN trong Wikipedia để biết chi tiết.

bool IsNan( float value ) { return ((*(UINT*)&value) & 0x7fffffff) > 0x7f800000; }


Tôi tin rằng điều này cũng sẽ làm việc trên các nền tảng lớn về cuối. Nghĩa đen 0x7fffffffchỉ đơn giản là ngồi trong bộ nhớ như ff ff ff 7f. valuecó thứ tự giống như vậy 0x7f800000, vì vậy tất cả các hoạt động xếp hàng (không có sự hoán đổi byte). Tôi sẽ quan tâm nếu ai đó có thể kiểm tra điều này trên một nền tảng lớn về cuối.
Bryan W. Wagner

0x7fff1234cũng là một NaN. Vậy là0xffffffff
Steve Hollasch

12

phòng chống nan

Câu trả lời của tôi cho câu hỏi này là không sử dụng kiểm tra hồi tố chonan . Sử dụng kiểm tra phòng ngừa cho các bộ phận của hình thức 0.0/0.0thay thế.

#include <float.h>
float x=0.f ;             // I'm gonna divide by x!
if( !x )                  // Wait! Let me check if x is 0
  x = FLT_MIN ;           // oh, since x was 0, i'll just make it really small instead.
float y = 0.f / x ;       // whew, `nan` didn't appear.

nankết quả từ hoạt động 0.f/0.f, hoặc 0.0/0.0. nanlà một kẻ thù khủng khiếp đối với sự ổn định của mã của bạn phải được phát hiện và ngăn chặn rất cẩn thận 1 . Các thuộc tính nankhác với số bình thường:

  • nanlà độc hại, (5 * nan= nan)
  • nankhông bằng bất cứ thứ gì, thậm chí không phải chính nó ( nan! = nan)
  • nankhông lớn hơn bất cứ điều gì ( nan!> 0)
  • nankhông ít hơn bất cứ điều gì ( nan! <0)

Hai thuộc tính cuối cùng được liệt kê là phản logic và sẽ dẫn đến hành vi kỳ lạ của mã dựa trên so sánh với một nansố (thuộc tính cuối cùng thứ 3 cũng là số lẻ nhưng có lẽ bạn sẽ không bao giờ nhìn thấy x != x ?trong mã của mình (trừ khi bạn đang kiểm tra cho nan (không đáng tin cậy))).

Trong mã của riêng tôi, tôi nhận thấy rằng nancác giá trị có xu hướng tạo ra các lỗi khó tìm. (Lưu ý làm thế nào đây không phải là trường hợp cho infhoặc -inf. ( -inf<0) trả về TRUE, (0 < inf) trả về TRUE và thậm chí ( -inf< inf) trả về TRUE. Vì vậy, theo kinh nghiệm của tôi, hành vi của mã thường vẫn như mong muốn).

Làm gì dưới nan

Những gì bạn muốn xảy ra 0.0/0.0 phải được xử lý như một trường hợp đặc biệt , nhưng những gì bạn làm phải phụ thuộc vào những con số bạn mong đợi sẽ ra khỏi mã.

Trong ví dụ trên , về cơ bản, kết quả của ( 0.f/FLT_MIN) sẽ là 0. Bạn có thể muốn 0.0/0.0tạo ra HUGEthay thế. Vì thế,

float x=0.f, y=0.f, z;
if( !x && !y )    // 0.f/0.f case
  z = FLT_MAX ;   // biggest float possible
else
  z = y/x ;       // regular division.

Vì vậy, ở trên, nếu x là 0.f, infsẽ có kết quả (có hành vi khá tốt / không phá hủy như đã đề cập ở trên thực tế).

Hãy nhớ rằng, phép chia số nguyên cho 0 gây ra ngoại lệ thời gian chạy . Vì vậy, bạn phải luôn kiểm tra phân chia số nguyên bằng 0. Chỉ vì 0.0/0.0lặng lẽ đánh giá nankhông có nghĩa là bạn có thể lười biếng và không kiểm tra 0.0/0.0trước khi nó xảy ra.

1 Kiểm tra nanthông qua x != xđôi khi không đáng tin cậy ( x != xbị loại bỏ bởi một số trình biên dịch tối ưu hóa phá vỡ sự tuân thủ của IEEE, đặc biệt là khi -ffast-mathbật công tắc).


Cảm ơn đã chỉ ra điều này; lập trình như thế chắc chắn sẽ giúp với vấn đề như vậy. Nhưng lần tới, vui lòng cố gắng không lạm dụng các tính năng định dạng văn bản quá nhiều. Thay đổi kích thước phông chữ, trọng lượng và phong cách như vậy đang làm cho nó thực sự khó đọc.
Magnus

4
Lưu ý rằng 0,0 / 0,0 không phải là hoạt động duy nhất có thể dẫn đến NaN. Căn bậc hai của một số âm trả về NaN. Cosin của + vô cực cũng trả về NaN. acos hoạt động (x) trong đó x không nằm trong phạm vi [0, pi] cũng có thể dẫn đến NaN. Tóm lại, người ta phải hết sức cẩn thận để xem xét các hoạt động tiềm ẩn rủi ro này, không chỉ đến 0,0 / 0,0.
Boris Dalstein

Hoàn toàn đồng ý với Boris. Theo kinh nghiệm của tôi, NaN thực tế luôn đến từ một cái gì đó như sqrt (-1.302e-53), tức là kết quả tính toán trung gian gần bằng không được đưa vào sqrt mà không kiểm tra độ âm.
hans_meine

1
"Ngăn chặn NaN" có nghĩa là bạn cần vào bên trong tất cả các hoạt động số học cơ bản, không chỉ phân chia. Bạn sẽ cần coi chừng /, 0 *,% x, x% 0, ∞ - ∞, 0 ^ 0, ∞ ^ 0, trong số nhiều người khác. Trở thành "phòng ngừa" với các hoạt động số học cơ bản như vậy có nghĩa là bạn sẽ hoàn toàn tăng hiệu suất của mình (và có thể bỏ lỡ các trường hợp bổ sung mà bạn không nghĩ tới).
Steve Hollasch

11

Kể từ C ++ 14, có một số cách để kiểm tra xem số dấu phẩy động valuecó phải là NaN hay không.

Trong số các cách này, chỉ kiểm tra các bit của biểu diễn số, hoạt động đáng tin cậy, như đã lưu ý trong câu trả lời ban đầu của tôi. Cụ thể, std::isnanvà kiểm tra thường được đề xuất v != v, không hoạt động đáng tin cậy và không nên được sử dụng, vì sợ rằng mã của bạn ngừng hoạt động chính xác khi ai đó quyết định rằng tối ưu hóa dấu phẩy động là cần thiết và yêu cầu trình biên dịch thực hiện điều đó. Tình huống này có thể thay đổi, trình biên dịch có thể trở nên phù hợp hơn, nhưng đối với vấn đề này đã không xảy ra trong 6 năm kể từ câu trả lời ban đầu.

Trong khoảng 6 năm, câu trả lời ban đầu của tôi là giải pháp được lựa chọn cho câu hỏi này, không sao cả. Nhưng gần đây, một câu trả lời được đánh giá cao khuyến nghị v != vthử nghiệm không đáng tin cậy đã được chọn. Do đó, câu trả lời cập nhật hơn này (hiện tại chúng tôi có các tiêu chuẩn C ++ 11 và C ++ 14 và C ++ 17 ở đường chân trời).


Các cách chính để kiểm tra NaN-ness, kể từ C ++ 14, là:

  • std::isnan(value) )
    là cách thư viện tiêu chuẩn dự định kể từ C ++ 11. isnanrõ ràng mâu thuẫn với macro Posix cùng tên, nhưng thực tế đó không phải là vấn đề. Vấn đề chính là khi yêu cầu tối ưu hóa số học dấu phẩy động, sau đó với ít nhất một trình biên dịch chính, cụ thể là g ++, std::isnan trả về falsecho đối số NaN .

  • (fpclassify(value) == FP_NAN) )
    Đau khổ từ cùng một vấn đề như std::isnan, không đáng tin cậy.

  • (value != value) )
    Đề xuất trong nhiều câu trả lời SO. Đau khổ từ cùng một vấn đề như std::isnan, không đáng tin cậy.

  • (value == Fp_info::quiet_NaN()) )
    Đây là một thử nghiệm mà với hành vi tiêu chuẩn không nên phát hiện NaN, nhưng với hành vi được tối ưu hóa có thể phát hiện ra NaN (do mã được tối ưu hóa chỉ so sánh trực tiếp các biểu diễn bitlevel) và có thể kết hợp với một cách khác để che đậy hành vi không được tối ưu hóa tiêu chuẩn , có thể phát hiện đáng tin cậy NaN. Thật không may, hóa ra nó không hoạt động đáng tin cậy.

  • (ilogb(value) == FP_ILOGBNAN) )
    Đau khổ từ cùng một vấn đề như std::isnan, không đáng tin cậy.

  • isunordered(1.2345, value) )
    Đau khổ từ cùng một vấn đề như std::isnan, không đáng tin cậy.

  • is_ieee754_nan( value ) )
    Đây không phải là một chức năng tiêu chuẩn. Đó là kiểm tra các bit theo tiêu chuẩn IEEE 754. Nó hoàn toàn đáng tin cậy nhưng mã phụ thuộc vào hệ thống.


Trong mã kiểm tra hoàn chỉnh sau đây, thành công, có phải là một biểu thức báo cáo giá trị Nan của giá trị hay không. Đối với hầu hết các biểu hiện, thước đo thành công này, mục tiêu phát hiện NaN và chỉ NaN, tương ứng với ngữ nghĩa chuẩn của chúng. (value == Fp_info::quiet_NaN()) )Tuy nhiên, đối với biểu thức, hành vi tiêu chuẩn là nó không hoạt động như một máy dò 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 ) );
}

Kết quả với g ++ (lưu ý lại rằng hành vi tiêu chuẩn của (value == Fp_info::quiet_NaN()) nó là nó không hoạt động như một máy dò NaN, nó thực sự rất được quan tâm ở đây):

[C: \ my \ forum \ so \ 282 (phát hiện NaN)]
> g ++ - đảo ngược | tìm "++"
g ++ (x86_64-win32-sjlj-rev1, Được xây dựng bởi dự án MinGW-W64) 6.3.0

[C: \ my \ forum \ so \ 282 (phát hiện NaN)]
> g ++ foo.cpp && a
Trình biên dịch xác nhận IEEE 754 = true

v = nan, (std :: isnan (value)) = true Thành công
u = 3.14, (std :: isnan (value)) = false Thành công
w = inf, (std :: isnan (value)) = false Thành công

v = nan, ((fp classify (value) == 0x0100)) = true Thành công
u = 3.14, ((fp classify (value) == 0x0100)) = false Thành công
w = inf, ((fp classify (value) == 0x0100)) = false Thành công

v = nan, ((value! = value)) = true Thành công
u = 3.14, ((value! = value)) = false Thành công
w = inf, ((value! = value)) = false Thành công

v = nan, ((value == Fp_info :: quiet_NaN ())) = sai FAILED
u = 3.14, ((value == Fp_info :: quiet_NaN ())) = sai Thành công
w = inf, ((value == Fp_info :: quiet_NaN ())) = sai Thành công

v = nan, ((ilogb (value) == ((int) 0x80000000))) = thành công thực sự
u = 3.14, ((ilogb (value) == ((int) 0x80000000))) = sai Thành công
w = inf, ((ilogb (value) == ((int) 0x80000000))) = sai Thành công

v = nan, (được sắp xếp lại (1.2345, giá trị)) = Thành công thực sự
u = 3.14, (được sắp xếp lại (1.2345, giá trị)) = sai Thành công
w = inf, (được sắp xếp lại (1.2345, giá trị)) = sai Thành công

v = nan, (is_ieee754_nan (value)) = Thành công thực sự
u = 3.14, (is_ieee754_nan (value)) = sai Thành công
w = inf, (is_ieee754_nan (value)) = sai Thành công

[C: \ my \ forum \ so \ 282 (phát hiện NaN)]
> g ++ foo.cpp -ffast-math && a
Trình biên dịch xác nhận IEEE 754 = true

v = nan, (std :: isnan (value)) = sai FAILED
u = 3.14, (std :: isnan (value)) = false Thành công
w = inf, (std :: isnan (value)) = false Thành công

v = nan, ((fp classify (value) == 0x0100)) = FAILED sai
u = 3.14, ((fp classify (value) == 0x0100)) = false Thành công
w = inf, ((fp classify (value) == 0x0100)) = false Thành công

v = nan, ((giá trị! = giá trị)) = sai FAILED
u = 3.14, ((value! = value)) = false Thành công
w = inf, ((value! = value)) = false Thành công

v = nan, ((value == Fp_info :: quiet_NaN ())) = thành công thực sự
u = 3.14, ((value == Fp_info :: quiet_NaN ())) = FAILED thật
w = inf, ((value == Fp_info :: quiet_NaN ())) = FAILED thật

v = nan, ((ilogb (value) == ((int) 0x80000000))) = thành công thực sự
u = 3.14, ((ilogb (value) == ((int) 0x80000000))) = sai Thành công
w = inf, ((ilogb (value) == ((int) 0x80000000))) = sai Thành công

v = nan, (được sắp xếp lại (1.2345, giá trị)) = sai FAILED
u = 3.14, (được sắp xếp lại (1.2345, giá trị)) = sai Thành công
w = inf, (được sắp xếp lại (1.2345, giá trị)) = sai Thành công

v = nan, (is_ieee754_nan (value)) = Thành công thực sự
u = 3.14, (is_ieee754_nan (value)) = sai Thành công
w = inf, (is_ieee754_nan (value)) = sai Thành công

[C: \ my \ forum \ so \ 282 (phát hiện NaN)]
> _

Kết quả với Visual C ++:

[C: \ my \ forum \ so \ 282 (phát hiện NaN)]
> cl / nologo- 2> & 1 | tìm "++"
Trình biên dịch tối ưu hóa Microsoft (R) C / C ++ Phiên bản 19.00.23725 cho x86

[C: \ my \ forum \ so \ 282 (phát hiện NaN)]
> cl foo.cpp / Tháng 2 && b
foo.cpp
Trình biên dịch xác nhận IEEE 754 = true

v = nan, (std :: isnan (value)) = true Thành công
u = 3.14, (std :: isnan (value)) = false Thành công
w = inf, (std :: isnan (value)) = false Thành công

v = nan, ((fp classify (value) == 2)) = true Thành công
u = 3.14, ((fp classify (value) == 2)) = false Thành công
w = inf, ((fp classify (value) == 2)) = false Thành công

v = nan, ((value! = value)) = true Thành công
u = 3.14, ((value! = value)) = false Thành công
w = inf, ((value! = value)) = false Thành công

v = nan, ((value == Fp_info :: quiet_NaN ())) = sai FAILED
u = 3.14, ((value == Fp_info :: quiet_NaN ())) = sai Thành công
w = inf, ((value == Fp_info :: quiet_NaN ())) = sai Thành công

v = nan, ((ilogb (value) == 0x7fffffff)) = Thành công thực sự
u = 3.14, ((ilogb (giá trị) == 0x7fffffff)) = sai Thành công
w = inf, ((ilogb (value) == 0x7fffffff)) = FAILED thật

v = nan, (được sắp xếp lại (1.2345, giá trị)) = Thành công thực sự
u = 3.14, (được sắp xếp lại (1.2345, giá trị)) = sai Thành công
w = inf, (được sắp xếp lại (1.2345, giá trị)) = sai Thành công

v = nan, (is_ieee754_nan (value)) = Thành công thực sự
u = 3.14, (is_ieee754_nan (value)) = sai Thành công
w = inf, (is_ieee754_nan (value)) = sai Thành công

[C: \ my \ forum \ so \ 282 (phát hiện NaN)]
> cl foo.cpp / Tháng 2 / fp: nhanh && b
foo.cpp
Trình biên dịch xác nhận IEEE 754 = true

v = nan, (std :: isnan (value)) = true Thành công
u = 3.14, (std :: isnan (value)) = false Thành công
w = inf, (std :: isnan (value)) = false Thành công

v = nan, ((fp classify (value) == 2)) = true Thành công
u = 3.14, ((fp classify (value) == 2)) = false Thành công
w = inf, ((fp classify (value) == 2)) = false Thành công

v = nan, ((value! = value)) = true Thành công
u = 3.14, ((value! = value)) = false Thành công
w = inf, ((value! = value)) = false Thành công

v = nan, ((value == Fp_info :: quiet_NaN ())) = sai FAILED
u = 3.14, ((value == Fp_info :: quiet_NaN ())) = sai Thành công
w = inf, ((value == Fp_info :: quiet_NaN ())) = sai Thành công

v = nan, ((ilogb (value) == 0x7fffffff)) = Thành công thực sự
u = 3.14, ((ilogb (giá trị) == 0x7fffffff)) = sai Thành công
w = inf, ((ilogb (value) == 0x7fffffff)) = FAILED thật

v = nan, (được sắp xếp lại (1.2345, giá trị)) = Thành công thực sự
u = 3.14, (được sắp xếp lại (1.2345, giá trị)) = sai Thành công
w = inf, (được sắp xếp lại (1.2345, giá trị)) = sai Thành công

v = nan, (is_ieee754_nan (value)) = Thành công thực sự
u = 3.14, (is_ieee754_nan (value)) = sai Thành công
w = inf, (is_ieee754_nan (value)) = sai Thành công

[C: \ my \ forum \ so \ 282 (phát hiện NaN)]
> _

Tóm tắt các kết quả trên, chỉ kiểm tra trực tiếp biểu diễn mức bit, sử dụng is_ieee754_nanhàm được xác định trong chương trình thử nghiệm này, hoạt động đáng tin cậy trong mọi trường hợp với cả g ++ và Visual C ++.


Phụ lục:
Sau khi đăng bài ở trên, tôi đã biết về một khả năng khác để kiểm tra NaN, được đề cập trong một câu trả lời khác ở đây, cụ thể là ((value < 0) == (value >= 0)). Điều đó hóa ra hoạt động tốt với Visual C ++ nhưng không thành công với -ffast-mathtùy chọn của g ++ . Chỉ kiểm tra bitpotype trực tiếp hoạt động đáng tin cậy.


7
inline bool IsNan(float f)
{
    const uint32 u = *(uint32*)&f;
    return (u&0x7F800000) == 0x7F800000 && (u&0x7FFFFF);    // Both NaN and qNan.
}

inline bool IsNan(double d)
{
    const uint64 u = *(uint64*)&d;
    return (u&0x7FF0000000000000ULL) == 0x7FF0000000000000ULL && (u&0xFFFFFFFFFFFFFULL);
}

Điều này hoạt động nếu sizeof(int)là 4 và sizeof(long long)là 8.

Trong thời gian chạy nó chỉ là so sánh, đúc không mất thời gian. Nó chỉ thay đổi cấu hình cờ so sánh để kiểm tra sự bình đẳng.


Cũng lưu ý, nó giới hạn ở đại diện của IEEE 754.
Chúc mừng và hth. - Alf

Lưu ý rằng dàn diễn viên này phá vỡ quy tắc bí danh nghiêm ngặt của g ++ và trình biên dịch đó đã được biết là thực hiện Unmentionable Things ™ khi phát hiện ra UB chính thức. Thay vì sử dụng phôi hiệu quả, với g ++ bạn cần sử dụng memcpy, thông qua một mảng byte để đảm bảo. Mã cho điều đó trong câu trả lời số 2 của tôi .
Chúc mừng và hth. - Alf

4

Một giải pháp khả thi không phụ thuộc vào đại diện cụ thể của IEEE cho NaN được sử dụng sẽ là:

template<class T>
bool isnan( T f ) {
    T _nan =  (T)0.0/(T)0.0;
    return 0 == memcmp( (void*)&f, (void*)&_nan, sizeof(T) );
}

Điểm nổi chính xác đơn có hơn 8 triệu phản hồi bit hợp pháp và khác nhau cho NaN, vì vậy bạn sẽ cần thêm một số so sánh. :)
Steve Hollasch

4

Xem xét rằng (x! = X) không phải lúc nào cũng được đảm bảo cho NaN (chẳng hạn như nếu sử dụng tùy chọn -ffast-math), tôi đã sử dụng:

#define IS_NAN(x) (((x) < 0) == ((x) >= 0))

Các số không thể là cả <0 và> = 0, vì vậy thực sự kiểm tra này chỉ vượt qua nếu số đó không nhỏ hơn, không lớn hơn hoặc bằng 0. Mà về cơ bản là không có số nào, hoặc NaN.

Bạn cũng có thể sử dụng điều này nếu bạn thích:

#define IS_NAN(x) (!((x)<0) && !((x)>=0)

Tôi không chắc chắn làm thế nào điều này bị ảnh hưởng bởi -ffast-math, vì vậy số dặm của bạn có thể thay đổi.


Điều này thực sự là thiếu sót theo cách tương tự f != flà thiếu sót. Tôi đã thấy llvm tối ưu hóa một đoạn mã gần như giống hệt nhau. Trình tối ưu hóa có thể truyền bá thông tin về so sánh đầu tiên và chỉ ra rằng phép tính thứ hai có thể không bao giờ đúng nếu kết quả đầu tiên là đúng. (nếu trình biên dịch tuân thủ nghiêm ngặt các quy tắc của IEEE f != fthì đơn giản hơn nhiều)
Markus

Không hoạt động với -ffast-mathtùy chọn của g ++ . Hoạt động với Visual C ++. Xem ( stackoverflow.com/a/42138465/464581 ).
Chúc mừng và hth. - Alf

3

Đối với tôi, giải pháp có thể là một macro để làm cho nó rõ ràng nội tuyến và do đó đủ nhanh. Nó cũng hoạt động cho bất kỳ loại nổi. Nó dựa trên thực tế là trường hợp duy nhất khi một giá trị không bằng chính nó là khi giá trị không phải là một số.

#ifndef isnan
  #define isnan(a) (a != a)
#endif

Đây là một trong những câu trả lời tốt nhất cho câu hỏi này! Cảm ơn bạn đã chia sẻ.
Henri Menke

2
Các câu trả lời khác chỉ ra rằng điều này có thể thất bại với tập tùy chọn -ffast-math.
Technophile

3

Những công việc này:

#include <iostream>
#include <math.h>
using namespace std;

int main ()
{
  char ch='a';
  double val = nan(&ch);
  if(isnan(val))
     cout << "isnan" << endl;

  return 0;
}

đầu ra: is Nam


1

Dường như với tôi, cách tiếp cận đa nền tảng thực sự tốt nhất sẽ là sử dụng liên kết và kiểm tra mô hình bit của double để kiểm tra NaN.

Tôi chưa thử nghiệm kỹ lưỡng giải pháp này và có thể có cách làm việc hiệu quả hơn với các mẫu bit, nhưng tôi nghĩ rằng nó nên hoạt động.

#include <stdint.h>
#include <stdio.h>

union NaN
{
    uint64_t bits;
    double num;
};

int main()
{
    //Test if a double is NaN
    double d = 0.0 / 0.0;
    union NaN n;
    n.num = d;
    if((n.bits | 0x800FFFFFFFFFFFFF) == 0xFFFFFFFFFFFFFFFF)
    {
        printf("NaN: %f", d);
    }

    return 0;
}

Lưu ý rằng "đó là hành vi không xác định để đọc từ thành viên của hiệp hội không được viết gần đây nhất". Vì vậy, việc sử dụng một unionkiểu chơi chữ giữa hai loại có thể không hoạt động như mong muốn (: sad_panda :). Cách chính xác (mặc dù không thực sự hoàn toàn di động như mong muốn) sẽ là tránh sự kết hợp hoàn toàn và thực hiện một memcpy từ doublemột uint64_tbiến khác , sau đó thực hiện kiểm tra bằng biến trợ giúp đó.
Eljay

0

Trên x86-64, bạn có thể có các phương pháp cực kỳ nhanh để kiểm tra NaN và vô cực, hoạt động bất kể -ffast-mathtùy chọn trình biên dịch. ( f != f, std::isnan, std::isinfLuôn mang lại falsevới -ffast-math).


Kiểm tra NaN, số vô hạn và số hữu hạn có thể dễ dàng được thực hiện bằng cách kiểm tra số mũ tối đa. vô cực là số mũ tối đa với mantissa bằng 0, NaN là số mũ tối đa và mantissa khác không. Số mũ được lưu trữ trong các bit tiếp theo sau bit dấu cao nhất, do đó chúng ta chỉ có thể dịch chuyển trái để loại bỏ bit dấu và làm cho số mũ là bit trên cùng, không cần che dấu ( operator&):

static inline uint64_t load_ieee754_rep(double a) {
    uint64_t r;
    static_assert(sizeof r == sizeof a, "Unexpected sizes.");
    std::memcpy(&r, &a, sizeof a); // Generates movq instruction.
    return r;
}

static inline uint32_t load_ieee754_rep(float a) {
    uint32_t r;
    static_assert(sizeof r == sizeof a, "Unexpected sizes.");
    std::memcpy(&r, &a, sizeof a); // Generates movd instruction.
    return r;
}

constexpr uint64_t inf_double_shl1 = UINT64_C(0xffe0000000000000);
constexpr uint32_t inf_float_shl1 = UINT32_C(0xff000000);

// The shift left removes the sign bit. The exponent moves into the topmost bits,
// so that plain unsigned comparison is enough.
static inline bool isnan2(double a)    { return load_ieee754_rep(a) << 1  > inf_double_shl1; }
static inline bool isinf2(double a)    { return load_ieee754_rep(a) << 1 == inf_double_shl1; }
static inline bool isfinite2(double a) { return load_ieee754_rep(a) << 1  < inf_double_shl1; }
static inline bool isnan2(float a)     { return load_ieee754_rep(a) << 1  > inf_float_shl1; }
static inline bool isinf2(float a)     { return load_ieee754_rep(a) << 1 == inf_float_shl1; }
static inline bool isfinite2(float a)  { return load_ieee754_rep(a) << 1  < inf_float_shl1; }

Các stdphiên bản isinfisfinitetải 2 double/floathằng số từ .dataphân khúc và trong trường hợp xấu nhất, chúng có thể gây ra 2 lỗi bộ nhớ cache dữ liệu. Các phiên bản trên không tải bất kỳ dữ liệu nào inf_double_shl1và các inf_float_shl1hằng số được mã hóa dưới dạng toán hạng ngay lập tức vào hướng dẫn lắp ráp.


Nhanh hơn isnan2chỉ là 2 hướng dẫn lắp ráp:

bool isnan2(double a) {
    bool r;
    asm(".intel_syntax noprefix"
        "\n\t ucomisd %1, %1"
        "\n\t setp %b0"
        "\n\t .att_syntax prefix"
        : "=g" (r)
        : "x" (a)
        : "cc"
        );
    return r;
}

Sử dụng thực tế là ucomisdlệnh đặt cờ chẵn lẻ nếu có bất kỳ đối số nào là NaN. Đây là cách std::isnanhoạt động khi không có -ffast-mathtùy chọn được chỉ định.


-1

Tiêu chuẩn IEEE cho biết khi số mũ là tất cả 1s và mantissa không bằng 0, số là a NaN. Double là 1bit dấu, 11bit lũy thừa và 52bit mantissa. Làm một chút kiểm tra.


-3

Như các bình luận ở trên trạng thái a! = A sẽ không hoạt động trong g ++ và một số trình biên dịch khác, nhưng thủ thuật này nên. Nó có thể không hiệu quả, nhưng nó vẫn là một cách:

bool IsNan(float a)
{
    char s[4];
    sprintf(s, "%.3f", a);
    if (s[0]=='n') return true;
    else return false;
}

Về cơ bản, trong g ++ (tôi không chắc chắn về những người khác) printf in các định dạng 'nan' trên các định dạng% d hoặc% .f nếu biến không phải là số nguyên / float hợp lệ. Do đó, mã này đang kiểm tra ký tự đầu tiên của chuỗi là 'n' (như trong "nan")


2
Điều đó có gây ra lỗi tràn bộ đệm không nếu a = 234324.0f?
Mazyod

Có, hoặc 340282346638528859811704183484516925440.000nếu a = FLT_MAX. Anh ta sẽ phải sử dụng char s[7]; sprintf(s, "%.0g", a);, sẽ là 6 chrs nếu a=-FLT_MAX, hoặc-3e+38
bobobobo

-3

Điều này phát hiện vô cực và cả NaN trong Visual Studio bằng cách kiểm tra nó trong giới hạn kép:

//#include <float.h>
double x, y = -1.1; x = sqrt(y);
if (x >= DBL_MIN && x <= DBL_MAX )
    cout << "DETECTOR-2 of errors FAILS" << endl;
else
    cout << "DETECTOR-2 of errors OK" << endl;

Kiểm tra định nghĩa của FLT_MIN, DBL_MINLDBL_MINcẩn thận hơn. Chúng được định nghĩa là các giá trị chuẩn hóa nhỏ nhất cho mỗi loại. Ví dụ: độ chính xác đơn có hơn 8 triệu giá trị biến dạng hợp pháp lớn hơn 0 và nhỏ hơn FLT_MIN(và không phải là NaN).
Steve Hollasch
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.