Câu hỏi Làm thế nào để sử dụng nan và inf trong C?


Tôi có một phương pháp số có thể trả về nan hoặc inf nếu có lỗi, và để thử nghiệm có mục đích tôi muốn tạm thời buộc nó quay trở lại nan hoặc inf để đảm bảo tình hình được xử lý đúng cách. Có đáng tin cậy không, độc lập với trình biên dịch cách để tạo ra giá trị của nan và inf trong C?

Sau khi googling trong khoảng 10 phút tôi đã chỉ có thể tìm thấy giải pháp phụ thuộc trình biên dịch.


76
2017-12-17 18:57


gốc


phao không được xác định theo tiêu chuẩn C. Vì vậy, không có cách biên dịch độc lập để làm những gì bạn muốn. - Johan Kotlinski


Các câu trả lời:


Bạn có thể kiểm tra xem việc triển khai của bạn có nó hay không:

#include <math.h>
#ifdef NAN
/* NAN is supported */
#endif
#ifdef INFINITY
/* INFINITY is supported */
#endif

Sự tồn tại của INFINITY được đảm bảo bởi C99 (hoặc bản nháp mới nhất ít nhất) và "mở rộng thành biểu thức liên tục của kiểu float thể hiện tích cực hoặc không dấu vô cùng, nếu có; khác với một hằng số dương của kiểu float mà tràn vào thời gian dịch. "

NAN có thể hoặc không được xác định, và "được xác định khi và chỉ khi việc thực hiện hỗ trợ NaN tĩnh cho kiểu float. Nó mở rộng đến một biểu thức liên tục của kiểu float đại diện cho một NaN tĩnh."

Lưu ý rằng nếu bạn so sánh các giá trị dấu phẩy động và thực hiện:

a = NAN;

thậm chí sau đó,

a == NAN;

là sai. Một cách để kiểm tra NaN là:

#include <math.h>
if (isnan(a)) { ... }

Bạn cũng có thể làm: a != a để kiểm tra xem a là NaN.

Cũng có isfinite(), isinf(), isnormal()signbit() macro trong math.h trong C99.

C99 cũng có nan chức năng:

#include <math.h>
double nan(const char *tagp);
float nanf(const char *tagp);
long double nanl(ocnst char *tagp);

(Tham khảo: n1256).


71
2017-12-17 19:15



Câu trả lời tuyệt vời. Tài liệu tham khảo cho các macro NAN và INFINITY là C99 §7.12 đoạn 4 và 5. Bên cạnh (isnan (a)), bạn cũng có thể kiểm tra NaN bằng cách sử dụng (a! = A) trên một thực hiện phù hợp của C. - Stephen Canon
Cảm ơn Stephen, đã cập nhật. - Alok Singhal
Vì tình yêu dễ đọc, a != a Nên KHÔNG BAO GIỜ được dùng. - Chris Kerekes
@ ChrisKerekes: thật đáng buồn, một số người trong chúng ta có NAN nhưng không phải isnan (). Vâng, đây là năm 2017. :( - eff


Không có trình biên dịch độc lập cách làm điều này, như không phải là C (cũng không phải là C + +) tiêu chuẩn nói rằng các loại toán học nổi điểm phải hỗ trợ NAN hoặc INF.

Chỉnh sửa: Tôi chỉ kiểm tra từ ngữ của tiêu chuẩn C ++, và nó nói rằng các hàm này (các thành viên của lớp templated số_limits):

quiet_NaN() 
signalling_NaN()

wiill trả lại các biểu diễn của NAN "nếu có". Nó không mở rộng về những gì "nếu có" có nghĩa là, nhưng có lẽ là một cái gì đó như "nếu thực hiện FP đại diện hỗ trợ họ". Tương tự, có một hàm:

infinity() 

trả về một đại diện INF tích cực "nếu có".

Cả hai đều được xác định trong <limits> tiêu đề - Tôi đoán rằng tiêu chuẩn C có một cái gì đó tương tự (có lẽ cũng "nếu có") nhưng tôi không có một bản sao của tiêu chuẩn C99 hiện hành.


33
2017-12-17 19:00



Đó là đáng thất vọng và đáng ngạc nhiên. Không C và C ++ phù hợp với các số dấu chấm động IEEE, có một đại diện tiêu chuẩn cho nan và inf? - Graphics Noob
C không ủy nhiệm các biểu diễn điểm nổi của IEEE. - Steve Emmerson
Trong C99, tiêu đề C <math.h> định nghĩa nan(), nanf()và nanl() trả về các biểu diễn NaN khác nhau (như một double, floatvà int tương ứng), và vô cùng (nếu có sẵn) có thể được trả lại bằng cách tạo ra một log(0) hay gì đó. Không có cách nào tiêu chuẩn để kiểm tra chúng, ngay cả trong C99. Các <float.h> tiêu đề (<limits.h> cho các loại không thể thiếu) inf và nan giá trị. - Chris Lutz
Wow, đó là một sự pha trộn lớn. nanl() trả về một long double, không phải là một int như bình luận của tôi nói. Tôi không biết tại sao tôi không nhận ra rằng khi tôi đang gõ nó. - Chris Lutz
@IngeHenriksen - Khá chắc chắn Microsoft đã tuyên bố rằng nó không có ý định của VC ++ hỗ trợ C99. - Chris Lutz


Một trình biên dịch độc lập, nhưng không phải là cách xử lý độc lập để có được:

int inf = 0x7F800000;
return *(float*)&inf;

int nan = 0x7F800001;
return *(float*)&nan;

Điều này sẽ hoạt động trên bất kỳ bộ xử lý nào sử dụng định dạng điểm nổi IEEE 754 (mà x86 thực hiện).

CẬP NHẬT: Đã kiểm tra và cập nhật.


19
2017-12-17 19:08



@ WaffleMatt - tại sao không phải cổng này giữa 32/64 bit? Ăng ten chính xác đơn IEEE 754 là 32-bit bất kể kích thước địa chỉ của bộ xử lý cơ bản. - Aaron
Đang truyền tới (float &) ? Điều đó không giống như C với tôi. Bạn cần int i = 0x7F800000; return *(float *)&i; - Chris Lutz
Lưu ý rằng 0x7f800001 là một cái gọi là báo hiệu NaN theo tiêu chuẩn IEEE-754. Mặc dù hầu hết các thư viện và phần cứng không hỗ trợ tín hiệu NaN, nhưng tốt hơn là nên trả về một NaN yên tĩnh như 0x7fc00000. - Stephen Canon
Cảnh báo: điều này có thể kích hoạt Hành vi không xác định thông qua vi phạm khử răng cưa nghiêm ngặt quy tắc. Đề nghị (và được hỗ trợ tốt nhất trong trình biên dịch) cách để làm loại punning đã qua thành viên Công đoàn. - ulidtko
Có thể sử dụng int32_t thay vì int, nếu có thể, để tương thích trong tương lai? - Keith M


Điều này làm việc cho cả hai float và double:

double NAN = 0.0/0.0;
double POS_INF = 1.0 /0.0;
double NEG_INF = -1.0/0.0;

Chỉnh sửa: Như ai đó đã nói, tiêu chuẩn IEEE cũ nói rằng các giá trị như vậy sẽ tăng bẫy. Nhưng các trình biên dịch mới hầu như luôn luôn tắt các bẫy và trả lại các giá trị đã cho bởi vì bẫy gây trở ngại xử lý.


18
2017-12-17 19:26



Bẫy là một lựa chọn để xử lý lỗi được phép theo 754-1985. Hành vi được sử dụng bởi hầu hết các phần cứng / trình biên dịch hiện đại cũng được cho phép (và là hành vi được ưa thích của nhiều thành viên trong ủy ban). Nhiều người triển khai không chính xác giả định rằng bẫy được yêu cầu do việc sử dụng thuật ngữ "ngoại lệ" không đáng tiếc trong tiêu chuẩn. Điều này đã được làm rõ rất nhiều trong sửa đổi 754-2008. - Stephen Canon
Xin chào, Stephen, bạn nói đúng, nhưng tiêu chuẩn cũng nói: "Người dùng sẽ có thể yêu cầu một cái bẫy trên bất kỳ năm ngoại lệ nào bằng cách chỉ định trình xử lý cho nó. Anh ấy có thể yêu cầu một trình xử lý hiện có bị vô hiệu hóa , cũng được lưu, hoặc khôi phục. Ông cũng có thể xác định liệu một trình xử lý bẫy cụ thể cho một ngoại lệ được chỉ định đã được bật hay chưa. " "nên" như được định nghĩa (2. Định nghĩa) có nghĩa là "được khuyến khích mạnh mẽ" và việc triển khai của nó chỉ nên được bỏ qua nếu kiến ​​trúc vv làm cho nó không thực tế. 80x86 hỗ trợ đầy đủ tiêu chuẩn, vì vậy không có lý do gì để C không hỗ trợ nó. - Thorsten S.
Tôi đồng ý rằng C nên yêu cầu điểm nổi 754 (2008), nhưng có lý do chính đáng để nó không; Cụ thể, C được sử dụng trong tất cả các loại môi trường khác với x86 - bao gồm các thiết bị nhúng không có điểm nổi phần cứng và thiết bị xử lý tín hiệu nơi người lập trình thậm chí không muốn sử dụng dấu chấm động. Đúng hay sai, những sử dụng đó chiếm nhiều quán tính trong đặc tả ngôn ngữ. - Stephen Canon
Tôi không biết tại sao câu trả lời hàng đầu lại đưa lên đó. Nó không cung cấp bất kỳ cách nào để tạo ra các giá trị được yêu cầu. Câu trả lời này có. - drysdam


double a_nan = strtod("NaN", NULL);
double a_inf = strtod("Inf", NULL);

12
2017-08-01 13:07



Đây là một giải pháp di động thông minh! C99 yêu cầu strtod và chuyển đổi NaN's và Inf's. - ulidtko
Không phải là có một nhược điểm với giải pháp này; chúng không phải là hằng số. Bạn không thể sử dụng các giá trị này để khởi tạo biến toàn cầu (ví dụ hoặc để khởi tạo một mảng). - Marc
Không làm việc cho tôi: VS 2008 chỉ trả về 0.0. - Keith M


<inf.h>

/* IEEE positive infinity.  */

#if __GNUC_PREREQ(3,3)
# define INFINITY   (__builtin_inff())
#else
# define INFINITY   HUGE_VALF
#endif

<bits/nan.h>
#ifndef _MATH_H
# error "Never use <bits/nan.h> directly; include <math.h> instead."
#endif


/* IEEE Not A Number.  */

#if __GNUC_PREREQ(3,3)

# define NAN    (__builtin_nanf (""))

#elif defined __GNUC__

# define NAN \
  (__extension__                                  \
   ((union { unsigned __l __attribute__ ((__mode__ (__SI__))); float __d; })  \
    { __l: 0x7fc00000UL }).__d)

#else

# include <endian.h>

# if __BYTE_ORDER == __BIG_ENDIAN
#  define __nan_bytes       { 0x7f, 0xc0, 0, 0 }
# endif
# if __BYTE_ORDER == __LITTLE_ENDIAN
#  define __nan_bytes       { 0, 0, 0xc0, 0x7f }
# endif

static union { unsigned char __c[4]; float __d; } __nan_union
    __attribute_used__ = { __nan_bytes };
# define NAN    (__nan_union.__d)

#endif  /* GCC.  */

3
2018-03-04 00:14





Tôi cũng ngạc nhiên rằng đây không phải là hằng số thời gian biên dịch. Nhưng tôi cho rằng bạn có thể tạo các giá trị này một cách dễ dàng bằng cách thực hiện một lệnh trả về kết quả không hợp lệ như vậy. Chia cho 0, log 0, tan 90, điều đó.


0
2017-12-17 19:09





Tôi thường sử dụng

#define INFINITY (1e999)

hoặc là

const double INFINITY = 1e999

hoạt động ít nhất trong bối cảnh IEEE 754 vì giá trị kép cao nhất có thể đại diện 1e308. 1e309 cũng sẽ hoạt động tốt 1e99999nhưng ba cái gai là đủ và đáng nhớ. Vì đây là một trong hai chữ (trong #define trường hợp) hoặc thực tế Inf giá trị, nó sẽ vẫn vô hạn ngay cả khi bạn đang sử dụng các phao nổi 128 bit ("dài gấp đôi").


0
2017-07-15 22:58



Điều này rất nguy hiểm, theo ý kiến ​​của tôi. Hãy tưởng tượng cách một người nào đó chuyển mã của bạn sang 128 bit nổi trong 20 năm hoặc lâu hơn (sau khi mã của bạn trải qua một quá trình tiến hóa phức tạp không thể tin được, không có giai đoạn nào bạn có thể dự đoán ngày hôm nay). Đột nhiên, phạm vi số mũ tăng lên đáng kể và tất cả 1e999 chữ không còn tròn +Infinity. Định luật của Per Murphy, điều này phá vỡ một thuật toán. Tồi tệ hơn: một lập trình viên của con người làm việc xây dựng "128-bit" sẽ không có khả năng phát hiện ra lỗi đó trước. I E. rất có thể se la qua muộn khi lỗi này được tìm thấy và nhận ra. Rất nguy hiểm. - ulidtko
Tất nhiên, trường hợp xấu nhất ở trên có thể xa thực tế. Nhưng vẫn còn, hãy xem xét các lựa chọn thay thế! Tốt hơn là ở bên an toàn. - ulidtko
"Trong 20 năm", heh. Nào. Câu trả lời này không cái đó xấu. - alecov
@ulidtko Tôi không thích điều này, nhưng thực sự? - Iharob Al Asimi
MSVC (16.0) tạo ra lỗi "hằng số quá lớn". - Andreas H.