Bagaimana cara menggunakan nan dan inf di C?

91

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.

Grafik Noob
sumber
pelampung tidak ditentukan oleh standar C. Jadi tidak ada cara independen-kompiler untuk melakukan apa yang Anda inginkan.
Johan Kotlinski

Jawaban:

87

Anda dapat menguji apakah implementasi Anda memilikinya:

Keberadaan INFINITYdijamin 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:

Anda juga dapat melakukan: a != auntuk menguji apakah aNaN.

Ada juga isfinite(), isinf(), isnormal(), dan signbit()makro di math.hdalam C99.

C99 juga memiliki nanfungsi:

(Referensi: n1256).

Docs INFINITY Docs NAN

Alok Singhal
sumber
2
Jawaban yang sangat bagus. Referensi untuk makro NAN dan INFINITY adalah C99 §7.12 paragraf 4 dan 5. Selain (isnan (a)) Anda juga dapat memeriksa NaN menggunakan (a! = A) pada implementasi yang sesuai dari C.
Stephen Canon
24
Untuk kasih mudah dibaca, a != aharus PERNAH digunakan.
Chris Kerekes
1
@ChrisKerekes: sayangnya, beberapa dari kita memiliki NAN tetapi tidak isnan (). Ya, ini 2017. :(
eff
C tidak membutuhkan, when ais a not-a-number, for a == NANto return false. IEEE membutuhkannya. Bahkan implementasi yang mematuhi IEEE, kebanyakan melakukannya . Jika isnan()tidak diterapkan, lebih baik membungkus pengujian daripada kode langsung a == NAN.
chux
34

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
Itu mengecewakan dan mengejutkan. Bukankah C dan C ++ sesuai dengan angka floating point IEEE, yang memiliki representasi standar untuk nan dan inf?
Grafik Noob
14
Dalam C99, C sundulan <math.h>mendefinisikan nan(), nanf()dan nanl()yang kembali representasi yang berbeda dari NaN (sebagai double, float, dan intmasing-masing), dan infinity (jika tersedia) bisa dikembalikan dengan menghasilkan satu dengan log(0)atau sesuatu. Tidak ada cara standar untuk memeriksanya, bahkan di C99. The <float.h>header ( <limits.h>adalah untuk jenis integral) sayangnya diam tentang infdan nannilai-nilai.
Chris Lutz
Wow, itu kesalahan besar. nanl()mengembalikan long double, bukan intseperti komentar saya. Saya tidak tahu mengapa saya tidak menyadarinya ketika saya sedang mengetiknya.
Chris Lutz
@ Chris, lihat jawaban saya untuk C99.
Alok Singhal
2
@IngeHenriksen - Cukup yakin Microsoft telah menyatakan bahwa ia tidak berniat VC ++ mendukung C99.
Chris Lutz
25

Ini berfungsi untuk keduanya floatdan double:

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.

Thorsten S.
sumber
Perangkap adalah salah satu opsi untuk penanganan kesalahan yang diizinkan dalam 754-1985. Perilaku yang digunakan oleh sebagian besar perangkat keras / kompiler modern juga diperbolehkan (dan merupakan perilaku yang disukai banyak anggota komite). Banyak pelaksana salah berasumsi bahwa trapping diperlukan karena penggunaan istilah "pengecualian" yang tidak menguntungkan dalam standar. Hal ini telah diperjelas dalam revisi 754-2008.
Stephen Canon
Hai, Stephen, Anda benar, tetapi standarnya juga mengatakan: "Pengguna harus dapat meminta jebakan pada salah satu dari lima pengecualian dengan menentukan penangan untuk itu. Dia harus dapat meminta penangan yang ada dinonaktifkan , disimpan, atau dipulihkan. Dia juga harus dapat menentukan apakah penangan perangkap khusus untuk pengecualian yang ditentukan telah diaktifkan. " "harus" sebagaimana didefinisikan (2. Definisi) berarti "sangat disarankan" dan implementasinya hanya boleh diabaikan jika arsitektur dll. membuatnya tidak praktis. 80x86 mendukung sepenuhnya standar tersebut, jadi tidak ada alasan bagi C untuk tidak mendukungnya.
Thorsten S.
Saya setuju bahwa C harus mensyaratkan titik mengambang 754 (2008), tetapi ada alasan bagus untuk tidak melakukannya; khusus, C digunakan di semua jenis lingkungan selain x86 - termasuk perangkat tertanam yang tidak memiliki titik-mengambang perangkat keras, dan perangkat pemroses sinyal di mana pemrogram bahkan tidak ingin menggunakan titik mengambang. Benar atau salah, penggunaan tersebut menyebabkan banyak inersia dalam spesifikasi bahasa.
Stephen Canon
Saya tidak tahu mengapa jawaban teratas ada di sana. Itu tidak memberikan cara apa pun untuk menghasilkan nilai yang diminta. Jawaban ini bisa.
drysdam
#define is_nan(x) ((x) != (x))semoga bermanfaat sebagai tes sederhana dan portabel untuk NAN.
Bob Stein
21

Cara independen kompilator, tetapi bukan cara independen prosesor untuk mendapatkannya:

Ini harus bekerja pada prosesor apa pun yang menggunakan format titik mengambang IEEE 754 (yang dilakukan x86).

UPDATE: Diuji dan diperbarui.

Aaron
sumber
2
@ WaffleMatt - mengapa port ini tidak antara 32/64 bit? Float presisi tunggal IEEE 754 adalah 32-bit terlepas dari ukuran pengalamatan prosesor yang mendasarinya.
Aaron
6
Mentransmisikan ke (float &)? Bagiku itu tidak terlihat seperti C. Anda membutuhkanint i = 0x7F800000; return *(float *)&i;
Chris Lutz
6
Perhatikan bahwa 0x7f800001yang disebut NaN pensinyalan dalam standar IEEE-754. Meskipun sebagian besar pustaka dan perangkat keras tidak mendukung NaN pensinyalan, kemungkinan lebih baik mengembalikan NaN seperti 0x7fc00000.
Stephen Canon
6
Peringatan: ini dapat memicu Perilaku Tidak Terdefinisi melalui pelanggaran aturan aliasing yang ketat . Cara yang direkomendasikan (dan paling didukung di compiler) untuk melakukan jenis punning adalah melalui anggota serikat .
ulidtko
2
Selain masalah aliasing ketat yang ditunjukkan @ulidtko, ini mengasumsikan bahwa target menggunakan endian yang sama untuk integer sebagai floating point, yang tentunya tidak selalu terjadi.
mr.stobbe
16
J. Kraftcheck
sumber
4
Ini adalah solusi portabel cerdas! C99 membutuhkan strtoddan memang mengubah NaN dan Inf.
ulidtko
1
Bukan berarti ada kekurangan dengan solusi ini; mereka bukanlah konstanta. Anda tidak dapat menggunakan nilai-nilai ini untuk menginisialisasi variabel global misalnya (atau untuk menginisialisasi array).
Marc
1
@Tokopedia Anda selalu dapat memiliki fungsi penginisialisasi yang memanggilnya sekali dan menyetelnya di namespace global. Ini adalah kelemahan yang bisa diterapkan.
Fisikawan Gila
3

dan

4pie0
sumber
0

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.

Carl Smotricz
sumber
0

Saya biasanya menggunakan

atau

yang berfungsi setidaknya dalam konteks IEEE 754 karena nilai ganda tertinggi yang dapat direpresentasikan kira-kira 1e308. 1e309akan bekerja sebaik mungkin 1e99999, tetapi tiga sembilan sudah cukup dan mudah diingat. Karena ini adalah literal ganda (dalam #definekasus ini) atau Infnilai sebenarnya , nilai ini akan tetap tidak terbatas meskipun Anda menggunakan float 128-bit ("ganda panjang").

Douglas Bagnall
sumber
1
Ini sangat berbahaya, menurut saya. Bayangkan bagaimana seseorang memigrasi kode Anda ke 128 bit float dalam 20 tahun atau lebih (setelah kode Anda mengalami evolusi yang luar biasa kompleks, tidak ada tahapan yang dapat Anda prediksi hari ini). Tiba-tiba, kisaran eksponen meningkat secara drastis, dan semua 1e999literal 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.
ulidtko
1
Tentu saja, skenario kasus terburuk di atas mungkin jauh dari realistis. Tapi tetap, pertimbangkan alternatifnya! Lebih baik tetap di sisi yang aman.
ulidtko
2
"Dalam 20 tahun", heh. Ayolah. Jawaban ini tidak terlalu buruk.
alecov
@ulidtko Saya juga tidak suka ini, tapi benarkah?
Iharob Al Asimi
0

Berikut adalah cara sederhana untuk menentukan konstanta tersebut, dan saya cukup yakin ini portabel:

Ketika saya menjalankan kode ini:

Saya mendapat:

Patrick Chkoreff
sumber