Saya memiliki metode numerik yang dapat mengembalikan nan atau inf jika ada kesalahan, dan untuk tujuan pengujian, saya ingin memaksanya untuk sementara mengembalikan nan atau inf untuk memastikan situasi ditangani dengan benar. Adakah cara yang andal dan tidak tergantung kompiler untuk membuat nilai nan dan inf di C?
Setelah googling sekitar 10 menit, saya hanya dapat menemukan solusi yang bergantung pada compiler.
Jawaban:
Anda dapat menguji apakah implementasi Anda memilikinya:
#include <math.h> #ifdef NAN /* NAN is supported */ #endif #ifdef INFINITY /* INFINITY is supported */ #endif
Keberadaan
INFINITY
dijamin oleh C99 (atau draf terbaru setidaknya), dan "meluas ke ekspresi konstan tipe float yang mewakili infinity positif atau tidak bertanda, jika tersedia; selain itu ke konstanta positif tipe float yang meluap pada waktu penerjemahan."NAN
mungkin atau mungkin tidak ditentukan, dan "ditentukan jika dan hanya jika implementasi mendukung NaN diam untuk jenis float. Ini meluas ke ekspresi konstan jenis float yang mewakili NaN diam."Perhatikan bahwa jika Anda membandingkan nilai floating point, dan lakukan:
bahkan kemudian,
salah. Salah satu cara untuk memeriksa NaN adalah:
#include <math.h> if (isnan(a)) { ... }
Anda juga dapat melakukan:
a != a
untuk menguji apakaha
NaN.Ada juga
isfinite()
,isinf()
,isnormal()
, dansignbit()
makro dimath.h
dalam C99.C99 juga memiliki
nan
fungsi:#include <math.h> double nan(const char *tagp); float nanf(const char *tagp); long double nanl(const char *tagp);
(Referensi: n1256).
Docs INFINITY Docs NAN
sumber
a != a
harus PERNAH digunakan.a
is a not-a-number, fora == NAN
to return false. IEEE membutuhkannya. Bahkan implementasi yang mematuhi IEEE, kebanyakan melakukannya . Jikaisnan()
tidak diterapkan, lebih baik membungkus pengujian daripada kode langsunga == NAN
.Tidak ada cara independen compiler untuk melakukan ini, karena baik standar C (maupun C ++) tidak mengatakan bahwa tipe matematika floating point harus mendukung NAN atau INF.
Sunting: Saya baru saja memeriksa kata-kata dari standar C ++, dan dikatakan bahwa fungsi-fungsi ini (anggota numeric_limits kelas templated):
akan mengembalikan representasi NAN "jika tersedia". Ini tidak memperluas tentang apa artinya "jika tersedia", tetapi mungkin sesuatu seperti "jika perwakilan FP pelaksana mendukung mereka". Demikian pula, ada fungsi:
yang mengembalikan perwakilan INF positif "jika tersedia".
Keduanya didefinisikan di
<limits>
header - saya akan menebak bahwa standar C memiliki sesuatu yang serupa (mungkin juga "jika tersedia") tetapi saya tidak memiliki salinan standar C99 saat ini.sumber
<math.h>
mendefinisikannan()
,nanf()
dannanl()
yang kembali representasi yang berbeda dari NaN (sebagaidouble
,float
, danint
masing-masing), dan infinity (jika tersedia) bisa dikembalikan dengan menghasilkan satu denganlog(0)
atau sesuatu. Tidak ada cara standar untuk memeriksanya, bahkan di C99. The<float.h>
header (<limits.h>
adalah untuk jenis integral) sayangnya diam tentanginf
dannan
nilai-nilai.nanl()
mengembalikanlong double
, bukanint
seperti komentar saya. Saya tidak tahu mengapa saya tidak menyadarinya ketika saya sedang mengetiknya.Ini berfungsi untuk keduanya
float
dandouble
:double NAN = 0.0/0.0; double POS_INF = 1.0 /0.0; double NEG_INF = -1.0/0.0;
Sunting: Seperti yang telah dikatakan seseorang, standar IEEE yang lama mengatakan bahwa nilai-nilai seperti itu harus meningkatkan jebakan. Tetapi kompiler baru hampir selalu mematikan traps dan mengembalikan nilai yang diberikan karena trapping mengganggu penanganan error.
sumber
#define is_nan(x) ((x) != (x))
semoga bermanfaat sebagai tes sederhana dan portabel untuk NAN.Cara independen kompilator, tetapi bukan cara independen prosesor untuk mendapatkannya:
int inf = 0x7F800000; return *(float*)&inf; int nan = 0x7F800001; return *(float*)&nan;
Ini harus bekerja pada prosesor apa pun yang menggunakan format titik mengambang IEEE 754 (yang dilakukan x86).
UPDATE: Diuji dan diperbarui.
sumber
(float &)
? Bagiku itu tidak terlihat seperti C. Anda membutuhkanint i = 0x7F800000; return *(float *)&i;
0x7f800001
yang disebut NaN pensinyalan dalam standar IEEE-754. Meskipun sebagian besar pustaka dan perangkat keras tidak mendukung NaN pensinyalan, kemungkinan lebih baik mengembalikan NaN seperti0x7fc00000
.double a_nan = strtod("NaN", NULL); double a_inf = strtod("Inf", NULL);
sumber
strtod
dan memang mengubah NaN dan Inf.<inf.h> /* IEEE positive infinity. */ #if __GNUC_PREREQ(3,3) # define INFINITY (__builtin_inff()) #else # define INFINITY HUGE_VALF #endif
dan
<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. */
sumber
Saya juga terkejut ini bukan konstanta waktu kompilasi. Tetapi saya kira Anda dapat membuat nilai-nilai ini dengan cukup mudah hanya dengan menjalankan instruksi yang mengembalikan hasil yang tidak valid. Membagi dengan 0, log dari 0, tan dari 90, hal semacam itu.
sumber
Saya biasanya menggunakan
#define INFINITY (1e999)
atau
const double INFINITY = 1e999
yang berfungsi setidaknya dalam konteks IEEE 754 karena nilai ganda tertinggi yang dapat direpresentasikan kira-kira
1e308
.1e309
akan bekerja sebaik mungkin1e99999
, tetapi tiga sembilan sudah cukup dan mudah diingat. Karena ini adalah literal ganda (dalam#define
kasus ini) atauInf
nilai sebenarnya , nilai ini akan tetap tidak terbatas meskipun Anda menggunakan float 128-bit ("ganda panjang").sumber
1e999
literal Anda tidak lagi dibulatkan+Infinity
. Sesuai hukum Murphy, ini merusak algoritme. Lebih buruk lagi: programmer manusia yang melakukan build "128-bit" tidak akan melihat kesalahan itu sebelumnya. Yaitu kemungkinan besar akan terlambat ketika kesalahan ini ditemukan dan dikenali. Sangat berbahaya.Berikut adalah cara sederhana untuk menentukan konstanta tersebut, dan saya cukup yakin ini portabel:
const double inf = 1.0/0.0; const double nan = 0.0/0.0;
Ketika saya menjalankan kode ini:
printf("inf = %f\n", inf); printf("-inf = %f\n", -inf); printf("nan = %f\n", nan); printf("-nan = %f\n", -nan);
Saya mendapat:
sumber