C'de nan ve inf nasıl kullanılır?


91

Bir hata olduğunda nan veya inf döndürebilen sayısal bir yöntemim var ve test amaçlı olarak, durumun doğru şekilde işlendiğinden emin olmak için onu geçici olarak nan veya inf döndürmeye zorlamak istiyorum. C'de nan ve inf değerlerini oluşturmanın güvenilir, derleyiciden bağımsız bir yolu var mı?

Yaklaşık 10 dakika boyunca googledikten sonra yalnızca derleyiciye bağlı çözümler bulabildim.


kayan değerler C standardı tarafından tanımlanmamıştır. Dolayısıyla, istediğinizi yapmanın derleyiciden bağımsız bir yolu yoktur.
Johan Kotlinski

Yanıtlar:


87

Uygulamanızda bulunup bulunmadığını test edebilirsiniz:

Varlığı INFINITYC99 (veya en azından en son taslak) ile garanti edilir ve "eğer mevcutsa pozitif veya işaretsiz sonsuzluğu temsil eden sabit bir float tipi ifadesine; aksi takdirde çeviri zamanında taşan pozitif bir float tipi sabitine genişler."

NAN tanımlanabilir veya tanımlanmayabilir ve "ancak ve ancak uygulama kayan tip için sessiz NaN'leri destekliyorsa tanımlanır. Sessiz bir NaN'yi temsil eden sabit bir kayan nokta türü ifadesine genişler."

Kayan nokta değerlerini karşılaştırıyorsanız ve şunları yapın:

o zaman bile,

yanlış. NaN'yi kontrol etmenin bir yolu şudur:

Ayrıca şunları da yapabilirsiniz: NaN a != aolup olmadığını test etmek için a.

Orada da isfinite(), isinf(), isnormal()ve signbit()makrolar içinde math.hC99.

C99 ayrıca şu nanişlevlere sahiptir :

(Referans: n1256).

Docs INFINITY Docs NAN


2
Mükemmel cevap. NAN ve INFINITY makroları için referans C99 §7.12 paragraf 4 ve 5'tir. Ayrıca (isnan (a)), C'ye uygun bir uygulamada (a! = A) kullanarak NaN'yi de kontrol edebilirsiniz.
Stephen Canon

24
Okunabilirliği aşkına, a != agerektiği ASLA kullanılabilir.
Chris Kerekes

1
@ChrisKerekes: Ne yazık ki, bazılarımızda NAN var ama isnan () yok. Evet, bu 2017. :(
eff

C asayı olmadığında a == NANfalse döndürmek için gerekli değildir. IEEE bunu gerektirir. IEEE'ye uyan uygulamalar bile bunu çoğunlukla yapar . Ne zaman isnan()uygulanmadı, hala daha iyi doğrudan kodundan testi sarmak için a == NAN.
chux

34

Bunu yapmanın derleyiciden bağımsız bir yolu yoktur, çünkü ne C (ne de C ++) standartları kayan noktalı matematik türlerinin NAN veya INF'yi desteklemesi gerektiğini söylemektedir.

Düzenleme: Sadece C ++ standardının ifadesini kontrol ettim ve bu işlevlerin (şablonlu sınıf sayısal_sınırlarının üyeleri):

"varsa" NAN temsillerini döndürecektir. "Varsa" nın ne anlama geldiğini açıklamıyor, ancak muhtemelen "uygulamanın FP temsilcisi onları destekliyorsa" gibi bir şey. Benzer şekilde, bir işlev var:

bu, "varsa" pozitif bir INF temsilcisi döndürür.

Bunların her ikisi de <limits>başlıkta tanımlanmıştır - C standardının benzer bir şeye sahip olduğunu tahmin ediyorum (muhtemelen "varsa" da), ancak mevcut C99 standardının bir kopyasına sahip değilim.


Bu hayal kırıklığı yaratıyor ve şaşırtıcı. C ve C ++, nan ve inf için standart bir temsili olan IEEE kayan nokta sayılarına uymuyor mu?
Grafik Noob

14
C99, C başlık olarak <math.h>tanımlayıp nan(), nanf()ve nanl()NaN bu dönüş farklı temsilleri (olarak double, floatve intsırası ile), ve sonsuz (yelpaze ise) ile bir üretilmesiyle döndürülebilir olabilir log(0)ya da bir şey. C99'da bile bunları kontrol etmenin standart bir yolu yoktur. <float.h>Başlık ( <limits.h>entegre türleri içindir) ile ilgili ne yazık ki sessizdir infve nandeğerler.
Chris Lutz

Vay canına, bu büyük bir karışıklık. nanl()a döndürüyor long double, intyorumumun söylediği gibi değil . Bunu yazarken neden fark etmediğimi bilmiyorum.
Chris Lutz

@Chris, C99 için cevabıma bakın.
Alok Singhal

2
@IngeHenriksen - Microsoft'un C99'u destekleyen VC ++ niyeti olmadığını belirttiğinden oldukça emin.
Chris Lutz

25

Bu ikisi için çalışır floatve double:

Düzenleme: Birisinin daha önce söylediği gibi, eski IEEE standardı bu tür değerlerin tuzakları yükseltmesi gerektiğini söyledi. Ancak yeni derleyiciler hemen hemen her zaman tuzakları kapatır ve verilen değerleri döndürür çünkü yakalama hata işlemeyi engeller.


Yakalama, 754-1985 altında izin verilen hata işleme seçeneklerinden biriydi. Çoğu modern donanım / derleyici tarafından kullanılan davranışa da izin verildi (ve komite üyelerinin çoğu için tercih edilen davranış buydu). Birçok uygulayıcı, standartta "istisnalar" teriminin talihsiz kullanımı nedeniyle yakalama işleminin gerekli olduğunu varsaydı. Bu, revize edilmiş 754-2008'de büyük ölçüde açıklığa kavuşturulmuştur.
Stephen Canon

Merhaba Stephen, haklısın ama standart aynı zamanda şunu da söylüyor: "Bir kullanıcı beş istisnadan herhangi biri için bir işleyici belirleyerek bir tuzak talep edebilmelidir. Mevcut bir işleyicinin devre dışı bırakılmasını talep edebilmelidir. , kaydedildi veya geri yüklendi. Ayrıca, belirlenmiş bir istisna için belirli bir tuzak işleyicisinin etkinleştirilip etkinleştirilmediğini de belirleyebilmelidir. " Tanımlandığı gibi "gerekir" (2. Tanımlar) "şiddetle tavsiye edilir" anlamına gelir ve uygulaması yalnızca mimari vb. uygulanamaz hale getirirse dışarıda bırakılmalıdır. 80x86 standardı tam olarak destekler, bu nedenle C'nin desteklememesi için hiçbir neden yoktur.
Thorsten S.

C'nin 754 (2008) kayan noktaya ihtiyaç duyması gerektiğine katılıyorum, ancak bunun olmaması için iyi nedenler var; Özellikle, C, donanım kayan noktası olmayan gömülü aygıtlar ve programcıların kayan nokta kullanmak istemediği sinyal işleme aygıtları dahil olmak üzere x86 dışındaki her tür ortamda kullanılır. Doğru veya yanlış olarak, bu kullanımlar dil spesifikasyonunda çok fazla eylemsizliği hesaba katar.
Stephen Canon

En iyi cevabın neden orada olduğunu bilmiyorum. İstenilen değerleri üretme imkanı yoktur. Bu cevap yapar.
drysdam

#define is_nan(x) ((x) != (x))NAN için basit, taşınabilir bir test olarak yararlı olabilir.
Bob Stein

21

Derleyiciden bağımsız bir yol, ancak bunları elde etmenin işlemciden bağımsız bir yolu:

Bu, IEEE 754 kayan nokta biçimini (x86'nın yaptığı) kullanan herhangi bir işlemci üzerinde çalışmalıdır.

GÜNCELLEME: Test edildi ve güncellendi.


2
@WaffleMatt - bu bağlantı noktası neden 32/64 bit olmasın? IEEE 754 tek duyarlıklı kayan nokta, temel işlemcinin adresleme boyutundan bağımsız olarak 32 bittir.
Aaron

6
Yayınlanıyor (float &)mu? Bu bana C gibi görünmüyor. İhtiyacın varint i = 0x7F800000; return *(float *)&i;
Chris Lutz

6
Bunun IEEE-754 standardında 0x7f800001sözde bir sinyalleşme NaN'si olduğuna dikkat edin. Çoğu kitaplık ve donanım sinyalizasyon NaN'lerini desteklemese de, sessiz bir NaN benzeri döndürmek muhtemelen daha iyidir 0x7fc00000.
Stephen Canon

6
Uyarı: Bu , katı takma ad kurallarının ihlali yoluyla Tanımsız Davranışı tetikleyebilir . Yapılması tavsiye (ve en iyi derleyici desteklenen) yolu tipi cinaslı geçer sendika üyeleri .
ulidtko

2
@Ulidtko'nun işaret ettiği katı takma ad sorununa ek olarak, bu, hedefin tamsayılar için aynı endian'ı kayan nokta olarak kullandığını varsayar ki bu kesinlikle her zaman böyle değildir.
mr.stobbe

16

4
Bu akıllı bir taşınabilir çözümdür! C99, NaN'leri strtodve Inf'leri gerektirir ve dönüştürür.
ulidtko

1
Bu çözümün bir dezavantajı olduğundan değil; sabit değillerdir. Bu değerleri, örneğin bir genel değişkeni başlatmak için (veya bir diziyi başlatmak için) kullanamazsınız.
Marc

1
@Marc. Her zaman, bunları bir kez çağıran ve bunları genel ad alanına ayarlayan bir başlatıcı işlevine sahip olabilirsiniz. Bu çok uygulanabilir bir dezavantaj.
Mad Fizikçi

3

ve


0

Bunların derleme zamanı sabitleri olmamasına da şaşırdım. Ancak, böyle geçersiz bir sonuç döndüren bir talimatı uygulayarak bu değerleri kolayca yaratabileceğinizi düşünüyorum. 0'a bölme, log 0, bronzluk 90, bu tür bir şey.


0

Genellikle kullanırım

veya

Bu, en azından IEEE 754 bağlamında çalışır, çünkü gösterilebilir en yüksek çift değer kabaca 1e308. 1e309aynen işe yarayacaktı 1e99999, ancak üç dokuz yeterli ve akılda kalıcı. Bu ya bir çift hazır bilgi ( #definedurumda) ya da gerçek bir Infdeğer olduğundan, 128 bitlik (“uzun çift”) kayan değerler kullansanız bile sonsuz kalacaktır.


1
Bu bence çok tehlikeli. Birisinin kodunuzu 20 veya daha fazla yıl içinde 128 bitlik kayan noktalara nasıl taşıdığını düşünün (kodunuz inanılmaz derecede karmaşık bir evrimden geçtikten sonra, bugün hiçbir aşamasını tahmin edemediğiniz). Aniden, üs aralığı büyük ölçüde artar ve tüm gerçek bilgileriniz 1e999artık yuvarlanmaz +Infinity. Murphy yasalarına göre, bu bir algoritmayı bozar. Daha da kötüsü: "128-bit" derlemeyi yapan bir insan programcı bu hatayı önceden fark etmeyecektir. Yani bu hata bulunup tanındığında büyük olasılıkla çok geç olacaktır . Çok tehlikeli.
ulidtko

1
Tabii ki, yukarıdaki en kötü durum senaryosu gerçekçi olmaktan uzak olabilir. Ama yine de alternatifleri düşünün! Güvenli tarafta kalmak daha iyidir.
ulidtko

2
"20 yıl sonra", heh. Haydi. Bu cevap o kadar da kötü değil .
alecov

@ulidtko Bunu da sevmiyorum, ama gerçekten?
Iharob Al Asimi

0

İşte bu sabitleri tanımlamanın basit bir yolu ve taşınabilir olduğundan oldukça eminim:

Bu kodu çalıştırdığımda:

Alırım:

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.