C ++ 'da çift (veya float) NaN olup olmadığını kontrol etme


368

İsnan () işlevi var mı?

Not: MinGW'deyim (bu bir fark yaratırsa ).

Bunu , ilk başta <math.h>içinde bulunduğum <cmath>, içinde bulunmadığım isnan () kullanarak çözmüştüm #include.


2
Saf değil portatif olarak yapabilirsiniz. Kim C ++ IEEE754 gerektirir diyor?
David Heffernan


Sadece bir not, 1 oz önleme 1 lb kürden daha iyidir. Başka bir deyişle, 0.f / 0.f'nin yürütülmesini engellemek, kodunuzdakinan 'leri geriye dönük olarak kontrol etmekten çok daha iyidir . nanprogramınız için çok yıkıcı olabilir, çoğalmasına izin verilirse hata bulmak zor getirebilir. Çünkü nantoksiktir (5 * nan= nan), nanhiçbir şeye eşit değildir ( nan! = nan), nanHiçbir şeyden daha büyük değildir ( nan!> 0), nanhiçbir şeyden daha az değildir ( nan! <0).
bobobobo

1
@bobobobo: Bu, merkezi hata kontrolüne izin veren bir özelliktir. Tıpkı dönüş değerlerine karşı istisnalar gibi.
Ben Voigt

2
Neden <cmath> isnan () içermiyor? Std içinde ::
frankliuao

Yanıtlar:


349

IEEE standardına göre, NaN değerleri, bunları içeren karşılaştırmalar her zaman yanlış olan garip özelliğe sahiptir . Yani, bir f şamandırası için, sadece f NaN ise f != fdoğrudur .

Aşağıdaki bazı yorumların belirttiği gibi, kod optimize ederken tüm derleyiciler buna saygı duymaz.

IEEE kayan noktalı kullanmak iddia herhangi bir derleyici için bu hile gerektiğini çalışır. Ama bunun garanti edemez olacak pratikte çalışır. Şüpheniz varsa derleyicinize danışın.


4
Derleyici, bir IEEE modunda çalışıyorsa bunu kaldırmamalıydı. Derleyicinizin belgelerine bakın, elbette ...
dmckee --- eski moderatör kedi yavrusu

38
-1 sadece teoride çalışır, pratikte değil: g ++ (-fastmath ile) gibi derleyiciler bunu berbat eder. c ++ 0x'a kadar tek genel yol bitpattern'i test etmektir.
Şerefe ve s. - Alf

66
@Alf: -ffast-mathSeçeneğin belgeleri açıkça IEEE veya matematik işlevleri için ISO kuralları / belirtimleri varsa tam bir uygulamaya bağlı programlar için yanlış çıktıya neden olabileceğini söylüyor. Bu seçenek etkinleştirilmezse, x != xNaN için geçerli ve taşınabilir bir test yöntemi kullanılır.
Adam Rosenfield

7
@Adam: Belgeler uygun olmadığını açıkça belirtiyor, evet. ve evet, bu tartışmayı daha önce Gabriel Dos Reis ile tartışarak karşılaştım. dairesel bir argümanda, tasarımı savunmak için yaygın olarak kullanılır (bununla ilişkilendirmeyi düşünüp düşünmediğinizi bilmiyorum, ama bilmeye değer - bu alev savaş şeyleri). x != xbu seçenek olmadan geçerli olan sonucunuz mantıklı gelmez. g ++ 'nın belirli bir sürümü için geçerli olabilir veya olmayabilir. her neyse, genellikle fastmath seçeneğinin kullanılmayacağını garanti etmenin hiçbir yolu yoktur.
Şerefe ve s. - Alf

7
@Alf: Hayır Gabriel Dos Reis ile olan tartışmanızın farkında değildim. Steve Jessop, IEEE temsilini varsaymakla ilgili diğer soruda büyük bir noktaya değindi. IEEE 754'ü kabul ederseniz ve derleyicinin uygun bir şekilde çalıştığını (yani -ffast-mathseçenek olmadan ) varsa, x != xgeçerli ve taşınabilir bir çözümdür. Hatta makroyu -ffast-mathtest ederek test edebilir __FAST_MATH__ve bu durumda farklı bir uygulamaya geçebilirsiniz (örneğin, sendikalar ve bit döndürme kullanın).
Adam Rosenfield

220

isnan()Geçerli C ++ Standart Kitaplığında kullanılabilir işlev yoktur . C99'da tanıtıldı ve bir işlev değil bir makro olarak tanımlandı . C99 tarafından tanımlanan standart kütüphanenin unsurları, mevcut C ++ standardı ISO / IEC 14882: 1998'in bir parçası değildir ve ISO / IEC 14882: 2003 güncellemesidir.

2005 yılında Teknik Rapor 1 önerilmiştir. TR1, C99 ile C ++ 'a uyumluluk getirir. C ++ standardı olmanın resmen kabul edilmemesine rağmen, çoğu ( GCC 4.0+ veya Visual C ++ 9.0+ C ++ uygulamaları hepsi veya sadece TR1 özellikleri sağlar (Visual C ++ 9.0 C99 matematik işlevleri sağlamaz) .

TR1 varsa, o zaman cmathgibi C99 unsurları içerir isnan(), isfinite()vb ancak genellikle işlevler değil makrolar olarak tanımlanır std::tr1::, ad olsa onlara iğne (Mac OS X 10.5+ üzerindeki XCode Linux üzerinde veya içinde yani GCC 4+) birçok uygulamaları doğrudan std::, yani std::isnaniyi tanımlanmış.

Dahası, bazı C ++ uygulamaları hala C99 isnan()makrosunu C ++ için kullanılabilir ( cmathveya aracılığıyla math.h), daha fazla karışıklığa neden olabilir ve geliştiriciler bunun standart bir davranış olduğunu varsayabilir.

Yukarıda da belirtildiği gibi Viusal C hakkında not ++, bu sağlamaz std::isnande std::tr1::isnan, ancak olarak tanımlanmış bir uzantı fonksiyonu sağlar _isnan()çünkü mevcut olmuştur ki Visual C ++ 6.0

XCode'da daha da eğlenceli. Belirtildiği gibi, GCC4+ tanımlamaktadır std::isnan. Derleyici ve kütüphane formunun eski sürümleri için XCode, Intel ve Power PC'de iki işlev tanımlanmış gibi görünüyor (burada ilgili tartışma var ), kendimi kontrol etme şansım olmadı .__inline_isnand()__isnand()


21
Herkes isNan veya isInfinity gibi bu işlevleri istiyor. Sorumlu insanlar neden sadece standartlarına dahil değiller ???? - Nasıl görev alabileceğimi öğrenmeye ve bunun için oyumu vermeye çalışacağım. Ciddi anlamda.
shuhalo

8
@shuhalo Henüz görevli misiniz?
Tomáš Zato - Monica'yı eski durumuna döndür

11
Bu yanıt std::isnan, C ++ 11 standardının bir parçası olduğu ve destek yayıldığı için güncellenmelidir. std :: isnan, Visual Studio 2013'ten başlayarak Visual Studio'da uygulandı. Belki @shuhalo görev aldı :-)
aberaud

170

İlk çözüm: C ++ 11 kullanıyorsanız

Bu sorulduğundan beri biraz yeni gelişmeler oldu: std::isnan()C ++ 11'in bir parçası olduğunu bilmek önemlidir

özet

Üstbilgide tanımlandı <cmath>

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

Verilen kayan nokta sayısı bağımsız değişkeninin bir sayı ( NaN) olup olmadığını belirler .

Parametreler

arg: kayan nokta değeri

Geri dönüş değeri

truearg ise NaN, falseaksi takdirde

Referans

http://en.cppreference.com/w/cpp/numeric/math/isnan

G ++ kullanıyorsanız bunun -fast-math ile uyumsuz olduğunu lütfen unutmayın, diğer öneriler için aşağıya bakın.


Diğer çözümler: C ++ 11 uyumlu olmayan araçlar kullanıyorsanız

C99 için, C'de, bu isnan(c)bir int değeri döndüren bir makro olarak uygulanır . Tipi xfloat, çift veya uzun çift olacaktır.

Çeşitli satıcılar bir işlev içerebilir veya içermeyebilir isnan().

Sözde taşınabilir olan kontrol etmenin yolu, kendisine eşit olmayan NaNIEEE 754 özelliğini kullanmaktır NaN: yani varlık x == xyanlıştır .xNaN

Ancak son seçenek her derleyici ve bazı ayarlarla (özellikle optimizasyon ayarları) çalışmayabilir, bu nedenle son çare olarak her zaman bit desenini kontrol edebilirsiniz ...


8
Kesinlikle kabul edilen cevap olmayı hak ediyor ve daha fazla oyu hak ediyor.
Tavsiye

3
-1 std::isnan bu gr ++ 'ın kayan nokta optimizasyonu ile çalışmaz, çünkü halen Şubat 2017 gibi bir ungood tavsiyedir.
Şerefe ve s. - Alf

@ Cheersandhth.-Alf: Bu seçenek IEEE uyumlu mu? Yanıt düzenlendi
BlueTrin

@BlueTrin: Her ikisi de x != xve isnanIEEE 754 uyumluluğu için çalışmak zorundadır. İkincisi ile ilgili olarak, IEEE 754-2008 standardı “Uygulamalar, desteklenen tüm aritmetik formatlar için aşağıdaki hesaplama dışı işlemleri sağlayacaktır” ve “isNaN (x), yalnızca x bir NaN ise doğrudur” şeklinde belirtmektedir. Standardın gerektirdiği is754version1985()ve is754version2008()bunun yerine C ++ 'ın sunduğu uygunluğu kontrol etmek için std::numeric_limits<Fp>::is_iec559()(IEC 559 aynı standarttır). Ne yazık ki -ffast-mathoptimizasyon ile, örneğin g ++ uygunluğu iddia eder, ancak uygun değildir.
Şerefe ve s. - Alf

1
Uyarı: isnan (x) sadece gcc ve clang'da -ffinite-math-seçeneği ile çalışmıyor
Bir Sis

82

Ayrıca, Boost'ta kayan nokta veri türleriyle başa çıkmak için düzgün araçlara sahip yalnızca başlık içeren bir kütüphane de vardır.

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

Aşağıdaki işlevleri alırsınız:

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);

Zamanınız varsa Boost'un tüm Matematik araç setine bir göz atın, birçok yararlı araç vardır ve hızla büyüyor.

Ayrıca, kayan ve kayan noktalarla uğraşırken Sayısal Dönüşümlere bakmak iyi bir fikir olabilir .


1
Teşekkürler! Tam da aradığım şey.
Watson

Boost 1.35'te eklendi (programımın eski linux dağıtımında derlenmediğini gördüm).
marcin

2
--fast-math seçeneğiyle derlerseniz bu işlev beklendiği gibi çalışmaz.
Gaetano Mendola

43

Üç "resmi" yol vardır: posix isnanmakrosu , c ++ 0x isnanişlev şablonu veya görsel c ++ _isnanişlevi .

Ne yazık ki hangilerinin kullanılacağını belirlemek oldukça pratik değildir.

Ne yazık ki, NaN'lerle IEEE 754 temsilciliğiniz olup olmadığını tespit etmenin güvenilir bir yolu yoktur. Standart kütüphane böyle bir görevli sunmaktadır ( numeric_limits<double>::is_iec559). Ama pratikte g ++ gibi derleyiciler bunu berbat ediyor.

Teoride basitçe kullanılabilir x != x, ancak g ++ ve visual c ++ gibi derleyiciler bunu berbat eder.

Sonuç olarak , IEEE 754 gibi belirli bir temsili varsayarak (ve umarım bir noktada zorlayarak!) Belirli NaN bit desenlerini test edin .


EDIT : "g ++… gibi vidalar derleyiciler" bir örnek olarak, düşünün

#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 );
}

G ++ (TDM-2 mingw32) ile derleme 4.4.1:

C: \ test> "C: \ Program Files \ @commands \ gnuc.bat" yazın
@rem -finput-charset = windows-1252
@ g ++ -O -pedantic -std = c ++ 98 -Duvar -Yazı-dizeleri% * -Wno-long-long

C: \ test> gnuc x.cpp

C: \ test> a && echo çalışıyor ... || echo! başarısız
İşler...

C: \ test> gnuc x.cpp - hızlı matematik

C: \ test> a && echo çalışıyor ... || echo! başarısız
Onaylama başarısız oldu: a! = B, dosya x.cpp, satır 6

Bu uygulama Runtime'dan alışılmadık bir şekilde sonlandırmasını istedi.
Daha fazla bilgi için lütfen uygulamanın destek ekibiyle iletişime geçin.
!başarısız oldu

C: \ test> _

4
@Alf: Örneğiniz, Mac OS X ve Linux'ta 4.0 ve 4.5 arasındaki çeşitli g ++ sürümlerinde beklediğim gibi çalışıyor. -ffast-mathSeçeneğin belgeleri açıkça IEEE veya matematik fonksiyonları için ISO kuralları / spesifikasyonları varsa tam bir uygulamaya bağlı programlar için yanlış çıktı ile sonuçlanabileceğini söylüyor. Bu seçenek etkinleştirilmezse, x != xNaN için geçerli ve taşınabilir bir test yöntemi kullanılır.
Adam Rosenfield

6
@Adam: Eksik olan şey, C ++ standardının floatlar için IEEE temsili veya matematik gerektirmemesidir. Man sayfasının size söylediği gibi, gcc -ffast-mathhala uygun bir C ++ uygulamasıdır (iyi, numeric_limits::is_iec559doğru olduğunu varsayarsak , Alf, bunu önermediğine rağmen yukarıdadır): IEEE'ye dayanan C ++ kodu taşınabilir C ++ değildir ve hiçbir hakkı yoktur. uygulamaların bunu sağlamasını beklemek.
Steve Jessop

5
Ve Alf doğru, hızlı test 4.3.4 üzerinde ve is_iec559ile doğrudur -ffast-math. Buradaki sorun, GCC'nin dokümanlarının -ffast-mathsadece matematik fonksiyonları için IEEE / ISO olmadığını söylerken, C ++ olmadığını söylemesi gerekir , çünkü uygulaması numeric_limitsborktur. GCC'nin her zaman şablonun tanımlandığı zamanda, nihai arka ucun gerçekten uygun yüzerlere sahip olup olmadığını söyleyemeyeceğini ve hatta denemeyeceğini tahmin ediyorum. IIRC, GCC'nin C99 uyumluluğu için olağanüstü hata listesinde benzer sorunlar var.
Steve Jessop

1
@Alf, @Steve, C ++ standardının kayan nokta değerleri hakkında hiçbir özelliği olmadığını bilmiyordum. Benim için oldukça şok edici. IEEE 754 ve NaN'yi standart yerine platforma özgü bir uzantı olarak ele almak daha iyi görünüyor. Öyle değil mi? Ve herhangi bir isnan () veya IEEE754 C ++ 0x eklenen bekleyebilir miyim?
Eonil

3
@Eonil: C ++ 0x hala örneğin "Kaplama noktası tiplerinin değer temsili uygulama olarak tanımlanmıştır". C ve C ++ her ikisi de kayan noktalı donanımı olmayan makinelerdeki uygulamaları desteklemeyi amaçlamaktadır ve uygun IEEE 754 şamandıralar taklit etmek makul doğruluktaki alternatiflerden biraz daha yavaş olabilir. Teori, is_iec559pratikte GCC üzerinde çalışmıyor gibi görünen IEEE'ye ihtiyacınız varsa iddia edebilirsiniz . C ++ 0x bir isnanişlevi var, ama GCC is_iec559şimdi doğru uygulamadığı için , ben de C ++ 0x de olmayacak ve -ffast-mathonun iyi kırabilir sanırım isnan.
Steve Jessop

39

Derleyici c99 uzantılarını destekliyorsa bir std :: isnan var, ancak mingw'un desteklediğinden emin değilim.

Derleyiciniz standart işleve sahip değilse çalışması gereken küçük bir işlev şunlardır:

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

6
neden sadece var! = var?
Brian R. Bondy

8
Bunu yaparken derleyici karşılaştırmayı optimize eder, her zaman doğru döner.
CTT

23
Hayır yok. Bunu yapan bir derleyici bozuldu. Standart kitaplığın isnanyanlış sonucu döndürme şansı olduğunu da söyleyebilirsiniz . Teknik olarak doğru, derleyici buggy olabilir , ama pratikte Olmayacak. İle aynı var != var. Bu çalışır çünkü IEEE kayan nokta değerleri bu şekilde tanımlanır.
jalf

29
-ffast-math ayarlanırsa, isnan () yöntemi gcc için doğru sonucu döndüremez. Tabii ki, bu optimizasyon IEEE anlambilimi kırma olarak belgelenmiştir ...
Matthew Herrmann

-Ffast-math ayarlanırsa, derleyici buggy olur. Daha doğrusu, -ffast-math belirlenmişse, tüm bahisler kapalıdır ve yine de NaN'lere güvenemezsiniz.
Adrian Ratnapala

25

Test etmek numeric_limits<float>::quiet_NaN( )için limitsstandart kitaplıkta tanımlanan kullanabilirsiniz . İçin ayrı bir sabit tanımlanmıştır 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;
}

Sadece Linux'ta g ++ ile test ettiğim için bunun tüm platformlarda çalışıp çalışmadığını bilmiyorum.


2
Yine de dikkat edin - quiet_aN için 0.0 döndürdüğü için, GCC 3.2.3 sürümünde sayısal_birimlerde bir hata var gibi görünüyor. GCC'nin sonraki sürümleri benim deneyimime uygun.
Nathan Kitchen

@Nathan: Bilmek güzel. Sürüm 4.3.2 kullanıyorum, bu yüzden ormandan iyi çıkıyorum.
Kertenkele Bill

18

isnan()İşlevi kullanabilirsiniz , ancak C matematik kitaplığını da eklemeniz gerekir.

#include <cmath>

Bu işlev C99'un bir parçası olduğundan, her yerde kullanılamaz. Satıcınız işlevi sağlamazsa, uyumluluk için kendi değişkeninizi de tanımlayabilirsiniz.

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

<cmath> kullanıyordum ve içinde isnan yok! tesadüfen orada öğrendim olduğu bir isnanyer <math.h>
Hasen

1
Dediğim gibi, bu C99'un bir parçası. C99 mevcut herhangi bir C ++ standardının bir parçası olmadığından, alternatifi sağladım. Ama isnan () yaklaşan bir C ++ standardına dahil edileceği için, etrafına #ifndef yönergesi koydum.
01'de raimue

12

Aşağıdaki kod, NAN tanımını (tüm üs bitleri kümesi, en az bir kesirli bit kümesi) kullanır ve sizeof (int) = sizeof (float) = 4 olduğunu varsayar. Ayrıntılar için Wikipedia'da NAN'a bakabilirsiniz.

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


Bunun büyük endian platformlarında da işe yarayacağına inanıyorum. Değişmez 0x7fffffffbasitçe bellekte otururdu ff ff ff 7f. valueile aynı sıralamaya sahiptir 0x7f800000, bu nedenle tüm işlemler sıralanır (bayt değişimi yoktur). Birinin bunu büyük bir endian platformunda test edip edemeyeceğini merak ediyorum.
Bryan W. Wagner

0x7fff1234aynı zamanda bir NaN'dir. Öyle0xffffffff
Steve Hollasch

12

nan önleme

Bu soruya cevabım geriye dönük kontroller kullanmamaknan . Kullanım önleyici formun bölümler için çek 0.0/0.0yerine.

#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.

nanoperasyondan elde edilen sonuçlar 0.f/0.fveya 0.0/0.0. çok dikkatli bir nanşekilde tespit edilmesi ve önlenmesi gereken kodunuzun kararlılığına korkunç bir düşmanıdır 1 . Bunun özellikleri nannormal sayılardan farklıdır:

  • nanzehirlidir, (5 * nan= nan)
  • nanhiçbir şeye eşit değil, kendisi bile değil ( nan! = nan)
  • nanhiçbir şeyden büyük değil ( nan!> 0)
  • nanhiçbir şeyden daha az değildir ( nan! <0)

Listelenen son 2 özellik karşı-mantıklıdır ve bir nansayı ile karşılaştırmaya dayanan kodun garip davranışı ile sonuçlanır (son 3 özellik de tuhaftır, ancak muhtemelen x != x ?kodunuzda görmeyeceksiniz (kontrol etmediğiniz sürece) nan için (güvenilmez))).

Kendi kodumda, nandeğerlerin bulmak zor hataları üretme eğiliminde olduğunu fark ettim . (Bunun veya durumunun nasıl olmadığına dikkat edin . ( <0) döndürür , (0 < ) TRUE döndürür ve hatta (inf-inf-infTRUEinf-inf < inf) TRUE döndürür. Bu nedenle, tecrübelerime göre, kodun davranışı genellikle hala istenildiği gibidir.

nan altında ne yapmalı

Altında ne olmak istiyorsun 0.0/0.0 özel bir durum olarak ele alınmalıdır , ancak yaptığınız şey koddan çıkmayı beklediğiniz sayılara bağlı olmalıdır.

Yukarıdaki örnekte, ( 0.f/FLT_MIN) sonucu 0temel olarak olacaktır . Bunun yerine 0.0/0.0oluşturmak isteyebilirsiniz HUGE. Yani,

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.

X olsaydı Yani yukarıda, 0.f, inf(aslında yukarıda belirtildiği gibi oldukça iyi / tahribatsız davranışa sahip olan) neden olur.

Unutmayın, 0'a tamsayı bölme bir çalışma zamanı istisnasına neden olur . Bu nedenle, her zaman 0 ile tamsayı bölünmesini kontrol etmelisiniz. Sadece 0.0/0.0sessizce değerlendirmek, nantembel olabileceğiniz anlamına gelmez ve 0.0/0.0gerçekleşmeden önce kontrol edemeyeceğiniz anlamına gelmez .

1 Geçiş kontrolleri bazen güvenilir değildir ( özellikle anahtar etkinleştirildiğinde IEEE uyumluluğunu bozan bazı optimize edici derleyiciler tarafından çıkarılır ).nanx != xx != x-ffast-math


Bunu işaret ettiğiniz için teşekkürler; bu şekilde programlama kesinlikle bu şekilde soruna yardımcı olacaktır. Ancak bir dahaki sefere, lütfen metin biçimlendirme özelliklerini çok fazla kötüye kullanmamaya çalışın. Yazı tipi boyutlarını, ağırlığını ve stilini değiştirmek, okumayı gerçekten zorlaştırıyor.
Magnus

4
NaN ile sonuçlanabilecek tek işlem 0.0 / 0.0 değildir. Negatif bir sayının kare kökü NaN değerini döndürür. + Sonsuzluğun kosinüsü NaN'yi de döndürür. x'in [0, pi] aralığında olmadığı işlem acos (x) da NaN ile sonuçlanabilir. Özetle, sadece 0.0 / 0.0'a değil, aynı zamanda bu riskli operasyonlara da bakmak için çok dikkatli olmak gerekir.
Boris Dalstein

Boris ile tamamen katılıyorum. Deneyimlerime göre, NaN neredeyse her zaman sqrt (-1.302e-53) gibi bir şeyden geldi, yani sıfıra yakın ara hesaplama sonuçları negatifliği kontrol etmeden sqrt'a besleniyor.
hans_meine

1
"NaN'leri Önlemek", sadece bölünme yerine tüm temel aritmetik işlemlerin içine girmeniz gerektiği anlamına gelir. Diğerlerinin yanı sıra ∞ / ∞, 0 * ∞, ∞% x, x% 0, ∞ - ∞, 0 ^ 0, ∞ ^ 0 için dikkat etmelisiniz. Bu tür temel aritmetik işlemlerle "önleyici" olmak, performansınızı tamamen depolayacağınız anlamına gelir (ve muhtemelen aklınıza gelmeyen ek vakaları kaçırırsınız).
Steve Hollasch

11

C ++ 14'ten itibaren bir kayan nokta sayısının test edilip edilmediğini test etmenin birkaç yolu vardır. value NaN .

Bu yollardan, sadece sayının temsilinin bitlerinin kontrol edilmesi, orijinal cevabımda belirtildiği gibi güvenilir bir şekilde çalışır. Özellikle std::isnanve sıkça önerilen çekv != v , güvenilir bir şekilde çalışmaz ve kullanılmamalıdır, eğer birisi kayan nokta optimizasyonunun gerekli olduğuna karar verdiğinde kodunuz düzgün çalışmayı durdurmaz ve derleyiciden bunu yapmasını ister. Bu durum değişebilir, derleyiciler daha uygun olabilir, ancak bu sorunun orijinal cevabından bu yana 6 yıl içinde gerçekleşmedi.

Yaklaşık 6 yıl boyunca asıl cevabım bu soru için seçilen çözümdü, ki bu iyiydi. Ancak son zamanlarda, güvenilir olmayan v != vtesti öneren oldukça güncel bir cevap seçildi. Dolayısıyla bu ek daha güncel cevap (artık C ++ 11 ve C ++ 14 standartlarına ve ufukta C ++ 17'ye sahibiz).


C ++ 14'ten itibaren NaN-ness'i kontrol etmenin ana yolları şunlardır:

  • std::isnan(value) )
    C ++ 11'den beri istenen standart kütüphane yoludur. isnanGörünüşe göre aynı adı taşıyan Posix makrosu ile çelişiyor, ancak pratikte bu bir sorun değil. Ana sorun, kayan nokta aritmetik optimizasyonu istendiğinde, en az bir ana derleyici, yani g ++ ile NaN argümanı için std::isnan geri falsedönmesidir .

  • (fpclassify(value) == FP_NAN) )
    std::isnanYani, aynı problemden muzdarip değildir, yani güvenilir değildir.

  • (value != value) )
    Birçok SO cevabında önerilir. std::isnanYani, aynı problemden muzdarip değildir, yani güvenilir değildir.

  • (value == Fp_info::quiet_NaN()) )
    Bu, standart davranışı olan NaN'leri algılamaması gereken bir testtir, ancak optimize edilmiş davranışla NaN'leri algılayabilir (optimize edilmiş kod sadece bit seviyesi sunumlarını doğrudan karşılaştırarak) ve belki de standart optimize edilmemiş davranışı kapsayacak başka bir yolla birleştirilebilir. , NaN'yi güvenilir bir şekilde tespit edebilir. Ne yazık ki güvenilir bir şekilde çalışmadığı ortaya çıktı.

  • (ilogb(value) == FP_ILOGBNAN) )
    std::isnanYani, aynı problemden muzdarip değildir, yani güvenilir değildir.

  • isunordered(1.2345, value) )
    std::isnanYani, aynı problemden muzdarip değildir, yani güvenilir değildir.

  • is_ieee754_nan( value ) )
    Bu standart bir işlev değil. Uçları IEEE 754 standardına göre kontrol ediyor. Tamamen güvenilirdir ancak kod bir şekilde sisteme bağlıdır.


Aşağıdaki tam test kodunda “başarı” ifadenin değerin Naneliğini rapor edip etmediğidir. Çoğu ifade için bu başarı ölçüsü, NaN'leri ve sadece NaN'leri tespit etme amacı, standart anlamlarına karşılık gelir. İçin (value == Fp_info::quiet_NaN()) )ekspresyon Ancak standart davranışı, NaN-dedektörü olarak çalışmaz olmasıdır.

#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 ) );
}

G ++ ile elde edilen sonuçlar (bunun standart davranışının (value == Fp_info::quiet_NaN())bir NaN-detektörü olarak çalışmadığına dikkat edin, buradaki pratik ilgi çok fazladır):

[C: \ my \ forumlar \ so \ 282 (NaN algıla)]
> g ++ - sürüm | "++" bul
g ++ (x86_64-win32-sjlj-rev1, MinGW-W64 projesi tarafından oluşturulmuştur) 6.3.0

[C: \ my \ forumlar \ so \ 282 (NaN algıla)]
> g ++ foo.cpp && a
Derleyici hak talepleri IEEE 754 = doğru

v = nan, (std :: isnan (değer)) = gerçek Başarı
u = 3.14, (std :: isnan (değer)) = yanlış Başarı
w = inf, (std :: isnan (değer)) = yanlış Başarı

v = nan, ((fpclassify (değer) == 0x0100)) = gerçek Başarı
u = 3.14, ((fpclassify (değer) == 0x0100)) = yanlış Başarı
w = inf, ((fpclassify (değer) == 0x0100)) = yanlış Başarı

v = nan, ((değer! = değer)) = gerçek Başarı
u = 3.14, ((değer! = değer)) = yanlış Başarı
w = inf, ((değer! = değer)) = yanlış Başarı

v = nan, ((değer == Fp_info :: quiet_NaN ())) = yanlış FAILED
u = 3.14, ((value == Fp_info :: quiet_NaN ())) = yanlış Başarı
w = inf, ((value == Fp_info :: quiet_NaN ())) = yanlış Başarı

v = nan, ((ilogb (değer) == ((int) 0x80000000))) = gerçek Başarı
u = 3.14, ((ilogb (değer) == ((int) 0x80000000))) = yanlış Başarı
w = inf, ((ilogb (değer) == ((int) 0x80000000))) = yanlış Başarı

v = nan, (değersiz (1.2345, değer)) = gerçek Başarı
u = 3.14, (değersiz (1.2345, değer)) = yanlış Başarı
w = inf, (isunordered (1.2345, değer)) = yanlış Başarı

v = nan, (is_ieee754_nan (değer)) = gerçek Başarı
u = 3.14, (is_ieee754_nan (değer)) = yanlış Başarı
w = inf, (is_ieee754_nan (değer)) = yanlış Başarı

[C: \ my \ forumlar \ so \ 282 (NaN algıla)]
> g ++ foo.cpp -ffast-math && a
Derleyici hak talepleri IEEE 754 = doğru

v = nan, (std :: isnan (değer)) = yanlış FAILED
u = 3.14, (std :: isnan (değer)) = yanlış Başarı
w = inf, (std :: isnan (değer)) = yanlış Başarı

v = nan, ((fpclassify (değer) == 0x0100)) = yanlış FAILED
u = 3.14, ((fpclassify (değer) == 0x0100)) = yanlış Başarı
w = inf, ((fpclassify (değer) == 0x0100)) = yanlış Başarı

v = nan, ((değer! = değer)) = yanlış FAILED
u = 3.14, ((değer! = değer)) = yanlış Başarı
w = inf, ((değer! = değer)) = yanlış Başarı

v = nan, ((value == Fp_info :: quiet_NaN ())) = gerçek Başarı
u = 3.14, ((value == Fp_info :: quiet_NaN ())) = doğru BAŞARISIZ
w = inf, ((value == Fp_info :: quiet_NaN ())) = doğru BAŞARISIZ

v = nan, ((ilogb (değer) == ((int) 0x80000000))) = gerçek Başarı
u = 3.14, ((ilogb (değer) == ((int) 0x80000000))) = yanlış Başarı
w = inf, ((ilogb (değer) == ((int) 0x80000000))) = yanlış Başarı

v = nan, (ayrıştırılmadı (1.2345, değer)) = yanlış FAILED
u = 3.14, (değersiz (1.2345, değer)) = yanlış Başarı
w = inf, (isunordered (1.2345, değer)) = yanlış Başarı

v = nan, (is_ieee754_nan (değer)) = gerçek Başarı
u = 3.14, (is_ieee754_nan (değer)) = yanlış Başarı
w = inf, (is_ieee754_nan (değer)) = yanlış Başarı

[C: \ my \ forumlar \ so \ 282 (NaN algıla)]
> _

Visual C ++ ile sonuçlar:

[C: \ my \ forumlar \ so \ 282 (NaN algıla)]
> cl / nologo- 2> & 1 | "++" bul
X86 için Microsoft (R) C / C ++ Optimize Edici Derleyici Sürümü 19.00.23725

[C: \ my \ forumlar \ so \ 282 (NaN algıla)]
> cl foo.cpp / Şub && b
foo.cpp
Derleyici hak talepleri IEEE 754 = doğru

v = nan, (std :: isnan (değer)) = gerçek Başarı
u = 3.14, (std :: isnan (değer)) = yanlış Başarı
w = inf, (std :: isnan (değer)) = yanlış Başarı

v = nan, ((fpclassify (değer) == 2)) = gerçek Başarı
u = 3.14, ((fpclassify (değer) == 2)) = yanlış Başarı
w = inf, ((fpclassify (değer) == 2)) = yanlış Başarı

v = nan, ((değer! = değer)) = gerçek Başarı
u = 3.14, ((değer! = değer)) = yanlış Başarı
w = inf, ((değer! = değer)) = yanlış Başarı

v = nan, ((değer == Fp_info :: quiet_NaN ())) = yanlış FAILED
u = 3.14, ((value == Fp_info :: quiet_NaN ())) = yanlış Başarı
w = inf, ((value == Fp_info :: quiet_NaN ())) = yanlış Başarı

v = nan, ((ilogb (değer) == 0x7fffffff)) = gerçek Başarı
u = 3.14, ((ilogb (değer) == 0x7fffffff)) = yanlış Başarı
w = inf, ((ilogb (değer) == 0x7fffffff)) = gerçek BAŞARISIZ

v = nan, (değersiz (1.2345, değer)) = gerçek Başarı
u = 3.14, (değersiz (1.2345, değer)) = yanlış Başarı
w = inf, (isunordered (1.2345, değer)) = yanlış Başarı

v = nan, (is_ieee754_nan (değer)) = gerçek Başarı
u = 3.14, (is_ieee754_nan (değer)) = yanlış Başarı
w = inf, (is_ieee754_nan (değer)) = yanlış Başarı

[C: \ my \ forumlar \ so \ 282 (NaN algıla)]
> cl foo.cpp / Şub / fp: hızlı && b
foo.cpp
Derleyici hak talepleri IEEE 754 = doğru

v = nan, (std :: isnan (değer)) = gerçek Başarı
u = 3.14, (std :: isnan (değer)) = yanlış Başarı
w = inf, (std :: isnan (değer)) = yanlış Başarı

v = nan, ((fpclassify (değer) == 2)) = gerçek Başarı
u = 3.14, ((fpclassify (değer) == 2)) = yanlış Başarı
w = inf, ((fpclassify (değer) == 2)) = yanlış Başarı

v = nan, ((değer! = değer)) = gerçek Başarı
u = 3.14, ((değer! = değer)) = yanlış Başarı
w = inf, ((değer! = değer)) = yanlış Başarı

v = nan, ((değer == Fp_info :: quiet_NaN ())) = yanlış FAILED
u = 3.14, ((value == Fp_info :: quiet_NaN ())) = yanlış Başarı
w = inf, ((value == Fp_info :: quiet_NaN ())) = yanlış Başarı

v = nan, ((ilogb (değer) == 0x7fffffff)) = gerçek Başarı
u = 3.14, ((ilogb (değer) == 0x7fffffff)) = yanlış Başarı
w = inf, ((ilogb (değer) == 0x7fffffff)) = gerçek BAŞARISIZ

v = nan, (değersiz (1.2345, değer)) = gerçek Başarı
u = 3.14, (değersiz (1.2345, değer)) = yanlış Başarı
w = inf, (isunordered (1.2345, değer)) = yanlış Başarı

v = nan, (is_ieee754_nan (değer)) = gerçek Başarı
u = 3.14, (is_ieee754_nan (değer)) = yanlış Başarı
w = inf, (is_ieee754_nan (değer)) = yanlış Başarı

[C: \ my \ forumlar \ so \ 282 (NaN algıla)]
> _

Yukarıdaki sonuçları özetlemek gerekirse is_ieee754_nan, bu test programında tanımlanan işlevi kullanarak sadece bit seviyesi gösteriminin doğrudan test edilmesi, hem g ++ hem de Visual C ++ ile her durumda güvenilir bir şekilde çalıştı.


Zeyilname:
Yukarıdakileri gönderdikten sonra, buradaki başka bir cevapta bahsedilen NaN'yi test etmek için bir başka ihtimal olduğunun farkına vardım ((value < 0) == (value >= 0)). Bu Visual C ++ ile iyi çalıştığı ortaya çıktı ama g ++ 'ın -ffast-mathseçeneği ile başarısız oldu . Sadece doğrudan bitpattern testi güvenilir bir şekilde çalışır.


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);
}

Bu sizeof(int), 4 ve sizeof(long long)8 ise çalışır .

Çalışma süresi boyunca sadece karşılaştırmadır, dökümler zaman almaz. Eşitliği kontrol etmek için karşılaştırma bayrakları yapılandırmasını değiştirir.


Ayrıca, IEEE 754 temsili ile sınırlı olduğunu unutmayın.
Şerefe ve s. - Alf

Bu dökümün g ++ 'ın katı takma kuralını ihlal ettiğini ve derleyicinin resmi UB algıladığında Unmentionable Things ™ yaptığı biliniyor. Etkili dökümler yerine, g ++ memcpyile emin olmak için bir bayt dizisi aracılığıyla kullanmanız gerekir . 2 numaralı cevabımda bunu kodla .
Şerefe ve s. - Alf

4

Kullanılan NaN için spesifik IEEE temsiline bağlı olmayacak olası bir çözüm aşağıdaki gibi olacaktır:

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

Tek hassasiyetli kayan nokta, NaN için 8 milyondan fazla meşru ve farklı bit temsiline sahiptir, bu nedenle biraz daha karşılaştırma eklemeniz gerekir. :)
Steve Hollasch

4

NaN için (x! = X) her zaman garanti edilmediğini göz önünde bulundurarak (örneğin, -ffast-math seçeneği kullanılıyorsa):

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

Sayılar hem <0 hem de> = 0 olamaz, bu nedenle bu denetim yalnızca sayı sıfırdan küçük, sıfırdan büyük veya ona eşitse geçer. Hangi temelde hiçbir sayı veya NaN.

İsterseniz bunu da kullanabilirsiniz:

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

Bunun -ffast-math'dan nasıl etkilendiğinden emin değilim, bu yüzden kilometreniz değişebilir.


Bu aslında aynı şekilde f != fkusurludur. Ben hemen hemen aynı kodu bir parça optimize llvm gördüm. Optimize edici, ilk karşılaştırma hakkındaki bilgileri yayabilir ve birincisi ise ikinci karşılaştırmanın asla doğru olmayabileceğini anlayabilir. (eğer derleyici kesinlikle IEEE kurallarına uyarsa f != f)
Markus

G ++ -ffast-mathseçeneği ile çalışmaz . Visual C ++ ile çalışır. Bkz. ( Stackoverflow.com/a/42138465/464581 ).
Şerefe ve s. - Alf

3

Bana gelince, çözüm onu ​​açıkça satır içi ve böylece yeterince hızlı hale getirmek için bir makro olabilir. Ayrıca herhangi bir şamandıra tipi için de çalışır. Bir değerin kendisine eşit olmadığı tek durumun değerin bir sayı olmadığı gerçeğine dayanmasıdır.

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

Bu, bu sorunun en iyi cevaplarından biridir! Paylaşım için teşekkürler.
Henri Menke

2
Diğer yanıtlar bunun -ffast-math seçenek kümesi ile başarısız olabileceğini göstermektedir.
Technophile

3

Bu çalışıyor:

#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;
}

çıktı: isnan


1

Bana göre en iyi gerçek çapraz platform yaklaşımı bir birlik kullanmak ve NaN'leri kontrol etmek için çiftin bit modelini test etmek olacaktır.

Bu çözümü tam olarak test etmedim ve bit desenleriyle çalışmanın daha verimli bir yolu olabilir, ancak bence işe yarayacak.

#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;
}

"En son yazılmamış sendika üyesinden okumak tanımlanmamış bir davranış" olduğunu unutmayın. Bu nedenle, unioniki tür arasında bir tip-pun için bu kullanım istendiği gibi çalışmayabilir (: sad_panda :). Doğru (aslında istendiği kadar taşınabilir olmasa da), birleşmeyi tamamen önlemek ve doublefarklı bir uint64_tdeğişkene bir memcpy yapmak, daha sonra bu yardımcı değişkeni kullanarak testi yapmak olacaktır.
Eljay

0

X86-64'te, -ffast-mathderleyici seçeneğinden bağımsız olarak çalışan NaN ve sonsuzluğu kontrol etmek için son derece hızlı yöntemlere sahip olabilirsiniz . ( f != f, std::isnan, std::isinfHer zaman verim falseile -ffast-math).


NaN, sonsuzluk ve sonlu sayılar için testler, maksimum üs kontrolü yapılarak kolayca yapılabilir. sonsuzluk sıfır mantis ile maksimum üs, NaN maksimum üs ve sıfır olmayan mantistir. Üs, en üstteki işaret bitinden sonra sonraki bitlerde saklanır, böylece işaret bitinden kurtulmak ve üssü en üst bitler yapmak için vardiyayı bırakabiliriz, maskeleme ( operator&) gerekmez:

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; }

stdSürümleri isinfve isfiniteyük 2 double/floatila sabitleri .datasegmenti ve en kötü ihtimalle onlar 2 veri önbellek isabetsizlik neden olabilir. Yukarıdaki sürümler herhangi bir veri yüklemez inf_double_shl1ve inf_float_shl1sabitler montaj talimatlarına derhal işlenen olarak kodlanır.


Daha hızlı isnan2sadece 2 montaj talimatıdır:

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;
}

ucomisdHerhangi bir argüman NaN ise komutun parite bayrağını ayarlamasını kullanır . std::isnanHiçbir -ffast-mathseçenek belirtilmediğinde bu şekilde çalışır .


-1

IEEE standardı, üs tümüyle 1s ve mantis sıfır olmadığında, sayının a olduğunu belirtir NaN. İkili 1işaret biti, 11üs bitleri ve 52mantis bitleridir. Biraz kontrol et.


-3

Yukarıdaki yorumlarda olduğu gibi a! = A, g ++ ve diğer bazı derleyicilerde çalışmaz, ancak bu hile gerekir. O kadar verimli olmayabilir, ama yine de bir yoludur:

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

Temel olarak, g ++ 'da (diğerlerinden emin değilim) printf, değişken geçerli bir tamsayı / float değilse% d veya% .f formatlarında' nan 'basar. Bu nedenle, bu kod dizenin ilk karakterinin 'n' olup olmadığını kontrol eder ("nan" de olduğu gibi)


2
A = 234324.0f ise arabellek taşmasına neden olmaz mı?
Mazyod

Evet, ya 340282346638528859811704183484516925440.000da = ise FLT_MAX. O kullanmak zorunda char s[7]; sprintf(s, "%.0g", a);olmadığını 6 chrs olmak which'll, a=-FLT_MAXya-3e+38
bobobobo

-3

Bu, Visual Studio'da sonsuzluğu ve ayrıca NaN'yi çift sınırlar içinde olduğunu kontrol ederek algılar:

//#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;

Tanımını kontrol FLT_MIN, DBL_MINve LDBL_MINdaha dikkatli. Bunlar, her tip için en küçük normalleştirilmiş değerler olarak tanımlanır . Örneğin, tek duyarlıklı sıfırdan büyük ve küçük FLT_MIN(NaN olmayan) 8 milyondan fazla meşru sınır değeri vardır .
Steve Hollasch
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.