यह जाँचना कि C ++ में कोई डबल (या फ्लोट) NaN है या नहीं


368

क्या कोई इस्न्नान () फ़ंक्शन है?

पुनश्च: मैं में हूँ मिनगव (यदि इससे कोई फर्क पड़ता है)।

मैंने इसे isnan () का उपयोग करके हल किया था <math.h>, जो इसमें मौजूद नहीं है <cmath>, जिसे मैं #includeपहली बार आईएनजी था ।


2
मैं शुद्ध नहीं हूँ आप इसे आंशिक रूप से कर सकते हैं। कौन कहता है कि C ++ को IEEE754 की आवश्यकता है?
डेविड हेफर्नन


बस एक नोट, रोकथाम का 1 औंस इलाज के 1 एलबी से बेहतर है। दूसरे शब्दों में, 0.f / 0.f को कभी भी निष्पादित होने से रोकनाnan आपके कोड में पूर्वव्यापी जाँच से कहीं बेहतर है । nanआपके कार्यक्रम के लिए बहुत विनाशकारी हो सकता है, अगर इसे फैलाने की अनुमति दी जाए तो यह कीड़े को खोजने के लिए कठिन परिचय दे सकता है। ऐसा इसलिए है क्योंकि nanविषाक्त है, (5 * nan= nan), nanकिसी भी चीज़ के बराबर नहीं है ( nan! =! nan), nanकिसी भी चीज़ से अधिक नहीं ( nan!> 0), nanकिसी भी चीज़ से कम नहीं है ( nan! <0)।
बोब्बोबो

1
@ बोबोबोबो: यह एक विशेषता है, जो केंद्रीकृत त्रुटि की जाँच करने की अनुमति देता है। जैसे अपवाद बनाम रिटर्न वैल्यू।
बेन वोइगट

2
क्यों <cmath> में isnan () नहीं है? यह std ::
frankliuao

जवाबों:


349

आईईईई मानक के अनुसार, NaN मूल्यों में विषम संपत्ति होती है, जिसमें उनकी तुलना करना हमेशा गलत होता है। अर्थात्, फ्लोट f के लिए, केवल तभीf != f सही होगा जब f NaN होगा।

ध्यान दें, जैसा कि नीचे दिए गए कुछ टिप्पणियों में बताया गया है, कोड का अनुकूलन करते समय सभी संकलक इसका सम्मान नहीं करते हैं।

किसी भी संकलक के लिए जो IEEE फ्लोटिंग पॉइंट का उपयोग करने का दावा करता है, इस ट्रिक को काम करना चाहिए । लेकिन मैं यह गारंटी नहीं दे सकता कि यह व्यवहार में काम करेगा । यदि संदेह हो तो अपने कंपाइलर से जांच लें।


4
IEEE मोड में चलने पर कंपाइलर ने इसे बेहतर तरीके से नहीं हटाया। अपने कंपाइलर के लिए डॉक्यूमेंटेशन जरूर देखें ...
dmckee --- ex-मॉडरेटर kitten

38
-1 केवल सिद्धांत में काम करता है, व्यवहार में नहीं: कंपाइलर जैसे कि जी ++ (-फास्टमैथ) पेंच। केवल सामान्य तरीका है, जब तक c ++ 0x, बिटपैटन के लिए परीक्षण करना है।
चीयर्स एंड हीथ। - अल्फ

66
@ शेल्फ: -ffast-mathविकल्प के लिए प्रलेखन स्पष्ट रूप से कहता है कि यह उन कार्यक्रमों के लिए गलत आउटपुट में परिणाम कर सकता है जो गणित के कार्यों के लिए IEEE या ISO नियमों / विशिष्टताओं पर सटीक कार्यान्वयन पर निर्भर करते हैं। इस विकल्प को सक्षम किए बिना, उपयोग x != xकरना NaN के लिए परीक्षण का एक बिल्कुल वैध और पोर्टेबल तरीका है।
एडम रोसेनफील्ड

7
@ एडम: प्रलेखन खुले तौर पर बताता है कि यह गैर-अनुरूप है, हाँ। और हाँ, मुझे उस तर्क का सामना करना पड़ा है, गैब्रियल डॉस रीस के साथ इस पर चर्चा करते हुए। यह आमतौर पर एक परिपत्र तर्क में डिजाइन का बचाव करने के लिए उपयोग किया जाता है (मुझे नहीं पता कि क्या आप उस से संबद्ध करना चाहते हैं, लेकिन इसके बारे में जानने के लायक है - यह लौ युद्ध सामान है)। आपका निष्कर्ष उस x != xविकल्प के बिना मान्य है, तार्किक रूप से पालन नहीं करता है। यह g ++ के किसी विशेष संस्करण के लिए सही हो सकता है, या नहीं। वैसे भी, आपके पास आम तौर पर यह गारंटी देने का कोई तरीका नहीं है कि फास्टमैथ विकल्प का उपयोग नहीं किया जाएगा।
चीयर्स एंड हीथ। - अल्फ

7
@ शेल्फ: नहीं मुझे गैब्रियल डॉस रीस के साथ आपकी चर्चा के बारे में पता नहीं था। आईईईई प्रतिनिधित्व ग्रहण करने के बारे में अन्य प्रश्न में स्टीव जेसोप ने एक महान बिंदु बनाया। यदि आप IEEE 754 मान लेते हैं और संकलक एक अनुरूप तरीके से काम कर रहा है (अर्थात -ffast-mathविकल्प के बिना ), तो x != xएक वैध और पोर्टेबल समाधान है। आप मैक्रो के -ffast-mathलिए परीक्षण करके भी जांच कर सकते हैं __FAST_MATH__और उस मामले में एक अलग कार्यान्वयन के लिए स्विच कर सकते हैं (जैसे यूनियनों और बिट ट्विडलिंग का उपयोग करें)।
एडम रोसेनफील्ड

220

isnan()वर्तमान C ++ मानक लाइब्रेरी में कोई फ़ंक्शन उपलब्ध नहीं है । इसे C99 में पेश किया गया था और इसे मैक्रो के रूप में परिभाषित किया गया था एक फ़ंक्शन नहीं था। C99 द्वारा परिभाषित मानक पुस्तकालय के तत्व वर्तमान C ++ मानक ISO / IEC 14882: 1998 का ​​हिस्सा नहीं हैं और न ही इसका अद्यतन ISO / IEC 14882: 2003 है।

2005 में तकनीकी रिपोर्ट 1 प्रस्तावित की गई थी। TR1 C99 के साथ C ++ के साथ संगतता लाता है। इस तथ्य के बावजूद कि इसे आधिकारिक तौर पर C ++ मानक बनने के लिए कभी नहीं अपनाया गया है, कई ( GCC 4.0+ या विज़ुअल C ++ 9.0+ C ++ कार्यान्वयन TR1 सुविधाएँ प्रदान करते हैं, उनमें से सभी या केवल कुछ (Visual C ++ 9.0 C99 मानक कार्य प्रदान नहीं करते हैं) ।

यदि TR1 उपलब्ध है, तो cmathजैसे C99 तत्व शामिल हैं isnan(), isfinite()आदि, लेकिन वे काम करता है, नहीं मैक्रो, आम तौर पर के रूप में परिभाषित कर रहे हैं std::tr1::, नाम स्थान हालांकि कई कार्यान्वयन (यानी जीसीसी 4 लिनक्स पर या XCode में पर मैक ओएस एक्स 10.5+) उन्हें इंजेक्षन सीधे std::, तो std::isnanअच्छी तरह से परिभाषित है।

इसके अलावा, C ++ के कुछ कार्यान्वयन अभी भी C ++ के लिए C99 isnan()मैक्रो उपलब्ध कराते हैं ( cmathया के माध्यम से शामिल हैं math.h), क्या अधिक भ्रम पैदा कर सकता है और डेवलपर्स मान सकते हैं कि यह एक मानक व्यवहार है।

Viusal C ++ के बारे में एक नोट, जैसा कि ऊपर उल्लेख किया गया है, यह std::isnanstd::tr1::isnanतो प्रदान करता है, लेकिन यह एक विस्तार फ़ंक्शन प्रदान करता है _isnan()जो कि तब से उपलब्ध है विजुअल C ++ 6.0 के

XCode पर, और भी मजेदार है। जैसा कि उल्लेख किया गया है, जीसीसी 4+ परिभाषित करता है std::isnan। कंपाइलर और लाइब्रेरी फॉर्म के पुराने संस्करणों के लिए XCode, ऐसा लगता है (यहां प्रासंगिक चर्चा है ), खुद को जांचने का मौका नहीं दिया है) दो फ़ंक्शन परिभाषित हैं, __inline_isnand()इंटेल __isnand()पर और पावर पीसी पर।


21
हर कोई इन कार्यों को चाहता है जैसे कि नॉन या इनफिनिटी। प्रभारी लोगों को केवल उनके मानकों में शामिल क्यों नहीं किया जाता है ???? - मैं यह पता लगाने की कोशिश करूंगा कि प्रभारी कैसे बनें और इसके लिए अपना वोट डालें। गंभीरता से।
शुऑलो

8
@shuhalo प्रभारी अभी तक?
टॉम ज़ातो -

11
यह उत्तर अपडेट किया जाना चाहिए क्योंकि std::isnanअब C ++ 11 मानक का हिस्सा है और समर्थन फैल गया है। std :: isnan विजुअल स्टूडियो में विजुअल स्टूडियो 2013 से शुरू किया गया था। शायद @shuhalo को चार्ज मिला :-)
aberaud

170

पहला समाधान: यदि आप C ++ 11 का उपयोग कर रहे हैं

चूंकि यह पूछा गया था कि नए घटनाक्रम के एक बिट थे: यह जानना महत्वपूर्ण है कि std::isnan()सी ++ 11 का हिस्सा है

सार

हेडर में परिभाषित <cmath>

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

निर्धारित करता है कि दिए गए फ्लोटिंग पॉइंट नंबर arg नंबर नहीं है ( NaN)।

पैरामीटर

arg: फ्लोटिंग पॉइंट वैल्यू

प्रतिलाभ की मात्रा

trueअगर arg है NaN, falseअन्यथा

संदर्भ

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

कृपया ध्यान दें कि यदि आप g ++ का उपयोग करते हैं, तो यह अन्य-सुझावों के लिए नीचे-असंगत है।


अन्य समाधान: यदि आप गैर सी ++ 11 अनुरूप उपकरणों का उपयोग कर रहे हैं

C99 के लिए, C में, इसे एक ऐसे मैक्रो के रूप में लागू किया जाता है isnan(c)जो एक अंतर मान लौटाता है। xफ्लोट का प्रकार डबल या लॉन्ग डबल होगा।

एक समारोह में विभिन्न विक्रेता शामिल हो सकते हैं या नहीं भी isnan()

जांच करने के लिए माना जाता है कि पोर्टेबल तरह से NaNआईईईई 754 संपत्ति है कि उपयोग करने के लिए है NaNही के बराबर नहीं है: यानी x == xके लिए झूठी हो जाएगा xजा रहा है NaN

हालांकि अंतिम विकल्प हर कंपाइलर और कुछ सेटिंग्स (विशेष रूप से अनुकूलन सेटिंग्स) के साथ काम नहीं कर सकता है, इसलिए अंतिम उपाय में, आप हमेशा बिटकॉइन की जांच कर सकते हैं ...


8
निश्चित रूप से स्वीकृत उत्तर के हकदार हैं और अधिक उत्थान के हकदार हैं। टिप के लिए धन्यवाद
LBs

3
−1 std::isnan अभी भी फरवरी 2017 के रूप में एक अनुचित सिफारिश है, क्योंकि यह जी ++ फ्लोटिंग पॉइंट ऑप्टिमाइज़ेशन के साथ काम नहीं करता है।
चीयर्स एंड हीथ। - अल्फा

@ Cheersandhth.-Alf: क्या यह विकल्प IEEE के अनुरूप है? उत्तर को संपादित किया गया है
ब्लूट्रिन

@ ब्ल्यूट्रिन: दोनों x != xऔर isnanIEEE 754 अनुपालन के लिए काम करने के लिए आवश्यक हैं। उत्तरार्द्ध के बारे में, IEEE 754-2008 मानक कहते हैं कि "कार्यान्वयन सभी समर्थित अंकगणितीय प्रारूपों के लिए निम्नलिखित गैर-कम्प्यूटेशनल संचालन प्रदान करेगा" और "NNN (x) सत्य है यदि x केवल एक NaN है "। अनुरूपता की जाँच के लिए उस मानक की आवश्यकता होती है is754version1985()और is754version2008(), जहाँ C ++ बजाय प्रदान करता है std::numeric_limits<Fp>::is_iec559()(IEC 559 समान मानक है)। दुर्भाग्य से -ffast-mathअनुकूलन के साथ , उदाहरण के लिए g ++ दावा अनुरूपता है, लेकिन गैर-अनुरूप है।
चीयर्स एंड हीथ। - अल्फ

1
चेतावनी: isnan (x) विकल्प के साथ काम नहीं करता है-अनंत-गणित-केवल gcc और clang में
A Fog

82

बूस्ट में एक हेडर-ओनली लाइब्रेरी भी मौजूद है जिसमें फ्लोटिंग पॉइंट डेटाैटिप्स से निपटने के लिए नीट टूल हैं

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

आपको निम्न कार्य मिलते हैं:

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

यदि आपके पास समय है तो बूस्ट से पूरे मैथ टूलकिट पर एक नज़र डालें, इसमें कई उपयोगी उपकरण हैं और जल्दी से बढ़ रहा है।

फ्लोटिंग और नॉन-फ्लोटिंग पॉइंट्स से निपटने के दौरान न्यूमेरिक कन्वर्सेशन को देखना एक अच्छा विचार हो सकता है ।


1
धन्यवाद! मुझे इसकी ही खोज थी।
डॉ। वॉटसन

इसे बूस्ट 1.35 में जोड़ा गया था (मैंने अभी पाया कि मेरा प्रोग्राम पुराने लिनक्स डिस्ट्रो पर संकलित नहीं है)।
मार्सिन

2
यदि आप विकल्प - रूप-गणित के साथ संकलन करते हैं तो यह फ़ंक्शन अपेक्षा के अनुरूप काम नहीं करेगा।
गेटानो मेंडोला

43

तीन "आधिकारिक" तरीके हैं: पॉज़िक्स isnanमैक्रो , सी ++ 0x isnanफ़ंक्शन टेम्प्लेट , या विज़ुअल सी ++ _isnanफ़ंक्शन

दुर्भाग्य से यह पता लगाने के लिए अव्यावहारिक है कि उनमें से कौन सा उपयोग करना है।

और दुर्भाग्य से, यह पता लगाने का कोई विश्वसनीय तरीका नहीं है कि आपके पास NaN के साथ IEEE 754 प्रतिनिधित्व है या नहीं। मानक पुस्तकालय इस तरह से एक अधिकारी प्रदान करता है (numeric_limits<double>::is_iec559 ) । लेकिन अभ्यास संकलक जैसे कि जी ++ स्क्रू अप।

सिद्धांत रूप में एक बस का उपयोग कर सकता है x != x, लेकिन कंपाइलर जैसे जी ++ और विज़ुअल सी ++ पेंच।

तो, अंत में, विशिष्ट NaN बिटपैटर्न के लिए परीक्षण , यह मानते हुए (और उम्मीद है कि लागू करने के लिए, कुछ बिंदु पर!) एक विशेष प्रतिनिधित्व जैसे IEEE 754।


संपादित करें : "कंपाइलर जैसे जी ++ ... स्क्रू दैट अप" के उदाहरण के रूप में, विचार करें

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

जी ++ (TDM-2 mingw32) के साथ संकलन 4.4.1:

C: \ test> टाइप करें "C: \ Program Files \ @commands \ gnuc.bat"
@ हर -फिनपुत-चारसेट = खिड़कियाँ -१२५२
@ g ++ -O-upantic -std = c ++ 98 -WallWiteite-strings% * -Wno-long

C: \ test> gnuc x.cpp

C: \ test> a && गूंज काम करता है ... || इको-फेल
काम करता है ...

C: \ test> gnuc x.cpp --fast-math

C: \ test> a && गूंज काम करता है ... || इको-फेल
अभिकथन विफल: a =! B, फ़ाइल x.cpp, पंक्ति 6

इस एप्लिकेशन ने रनटाइम से असामान्य तरीके से इसे समाप्त करने का अनुरोध किया है।
अधिक जानकारी के लिए कृपया आवेदन की सहायता टीम से संपर्क करें।
!अनुत्तीर्ण होना

C: \ test> _

4
@ शेल्फ: आपका उदाहरण मेरे लिए मैक ओएस एक्स और लिनक्स दोनों पर 4.0 और 4.5 के बीच जी ++ के विभिन्न संस्करणों पर अपेक्षित है। -ffast-mathविकल्प के लिए प्रलेखन स्पष्ट रूप से कहता है कि यह उन कार्यक्रमों के लिए गलत आउटपुट में परिणाम कर सकता है जो एक सटीक कार्यान्वयन पर निर्भर करते हैं यदि IEEE या ISO नियम / गणित कार्यों के लिए विनिर्देशन। इस विकल्प को सक्षम किए बिना, उपयोग x != xकरना NaN के लिए परीक्षण का एक बिल्कुल वैध और पोर्टेबल तरीका है।
एडम रोसेनफील्ड

6
@ एडडम: आप जो याद कर रहे हैं वह यह है कि सी ++ मानक को तैरने के लिए आईईईई प्रतिनिधित्व या गणित की आवश्यकता नहीं है। जहाँ तक मैन पेज आपको बताता है, gcc -ffast-mathअभी भी एक अनुरूप C ++ कार्यान्वयन है (ठीक है, यह मानते हुए कि यह numeric_limits::is_iec559सही हो जाता है, हालांकि, यह ऊपर अल्फ सुझाव देता है कि यह नहीं है): IEEE पर निर्भर C ++ कोड पोर्टेबल C ++ नहीं है और कोई अधिकार नहीं है इसे प्रदान करने के लिए कार्यान्वयन की अपेक्षा करना।
स्टीव जेसप

5
और गल्फ 4.3.4 पर अल्फ का सही, त्वरित परीक्षण और के is_iec559साथ सच है -ffast-math। तो यहां समस्या यह है कि जीसीसी के डॉक्स -ffast-mathकेवल यह कहते हैं कि यह गणित के कार्यों के लिए गैर-आईईईई / आईएसओ है, जबकि उन्हें कहना चाहिए कि यह गैर-सी ++ है, क्योंकि इसका कार्यान्वयन numeric_limitsबोर हो गया है। मुझे लगता है कि जीसीसी हमेशा उस समय को नहीं बता सकता है कि टेम्पलेट को परिभाषित किया गया है, चाहे अंतिम बैकएंड में वास्तव में अनुरूपता हो, और इसलिए यह भी कोशिश नहीं करता है। IIRC GCC के C99 अनुरूपता के लिए बकाया बग सूची में समान मुद्दे हैं।
स्टीव जेसोप

1
@Alf, @Steve, मुझे नहीं पता कि C ++ मानक में फ्लोटिंग-पॉइंट मानों के बारे में कोई विनिर्देश नहीं है। यह मेरे लिए काफी चौंकाने वाला है। यह मानक की बजाय IEEE 754 और NaN को प्लेटफ़ॉर्म विशिष्ट एक्सटेंशन के रूप में बेहतर तरीके से हैंडल करता है। है ना? और क्या मैं C ++ 0x में जोड़े गए किसी भी प्रकार के isnan () या IEEE754 की उम्मीद कर सकता हूं?
Eonil

3
@ ईऑनिल: C ++ 0x में अभी भी उदाहरण है "ओटिंग-पॉइंट प्रकारों का मूल्य प्रतिनिधित्व कार्यान्वयन-डे de नेड" है। C और C ++ दोनों का उद्देश्य बिना फ्लोटिंग-पॉइंट हार्डवेयर वाली मशीनों पर क्रियान्वयन का समर्थन करना है, और उचित IEEE 754 फ़्लोट यथोचित-सटीक विकल्पों की तुलना में काफी धीमा हो सकता है। सिद्धांत यह है कि is_iec559यदि आप जीईई पर काम करने के लिए प्रकट नहीं होते हैं, तो आपको IEEE की आवश्यकता है। C ++ 0x में एक isnanफ़ंक्शन होता है, लेकिन चूंकि GCC is_iec559अब सही ढंग से लागू नहीं होता है , मुझे लगता है कि यह C ++ 0x में भी नहीं होगा, और -ffast-mathअच्छी तरह से इसे तोड़ सकता है isnan
स्टीव जेसोप

39

एक std :: isnan है यदि आप संकलक c99 एक्सटेंशन का समर्थन करते हैं, लेकिन मुझे यकीन नहीं है कि अगर mingw करता है।

यहाँ एक छोटा सा फ़ंक्शन है जो काम करना चाहिए यदि आपके कंपाइलर में मानक फ़ंक्शन नहीं है:

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

6
सिर्फ var =! var क्यों नहीं?
ब्रायन आर। बॉन्डी

8
जब ऐसा कर रहे हैं कि एक मौका संकलक तुलना बाहर अनुकूलन, हमेशा सच वापस आ जाएगा।
सीटीटी

23
नहीं वहाँ नहीं है। एक कंपाइलर जो करता है वह टूट जाता है। आप यह भी कह सकते हैं कि एक मौका है कि मानक पुस्तकालय isnanगलत परिणाम देता है। तकनीकी रूप से सच है, कंपाइलर छोटी गाड़ी हो सकती है, लेकिन व्यवहार में, नॉट गोना हैपन। के रूप में ही var != var। यह इसलिए काम करता है क्योंकि IEEE फ्लोटिंग पॉइंट वैल्यू को परिभाषित किया गया है।
jalf

29
यदि -ffast-math सेट है, तो isnan () gcc के लिए सही परिणाम देने में विफल रहेगा। बेशक, इस अनुकूलन को IEEE शब्दार्थ को तोड़ने के रूप में प्रलेखित किया गया है ...
मैथ्यू हेरमैन 1

यदि -ffast-math सेट है, तो कंपाइलर छोटी गाड़ी है। या इसके बजाय, अगर -ffast- गणित सेट है, तो सभी दांव बंद हैं और आप किसी भी तरह NaN पर भरोसा नहीं कर सकते।
एड्रियन रत्नपाल

25

आप परीक्षण करने के numeric_limits<float>::quiet_NaN( )लिए limitsमानक पुस्तकालय में परिभाषित उपयोग कर सकते हैं । के लिए एक अलग स्थिरांक परिभाषित है 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;
}

मुझे नहीं पता कि यह सभी प्लेटफार्मों पर काम करता है, जैसा कि मैंने केवल लिनक्स पर जी ++ के साथ परीक्षण किया था।


2
बाहर देखें, हालांकि - जीसीसी संस्करण 3.2.3 में संख्यात्मक_लिमिट्स में एक बग प्रतीत होता है, क्योंकि यह 0.0_NaN के लिए वापस लौटता है। मेरे अनुभव में जीसीसी के बाद के संस्करण ठीक हैं।
नाथन रसोई

@ नथन: जानकर अच्छा लगा। मैं 4.3.2 संस्करण का उपयोग कर रहा हूं, इसलिए मैं जंगल से अच्छी तरह बाहर हूं।
छिपकली का बिल

18

आप isnan()फ़ंक्शन का उपयोग कर सकते हैं , लेकिन आपको सी गणित पुस्तकालय शामिल करने की आवश्यकता है।

#include <cmath>

चूंकि यह फ़ंक्शन C99 का हिस्सा है, इसलिए यह हर जगह उपलब्ध नहीं है। यदि आपका विक्रेता फ़ंक्शन की आपूर्ति नहीं करता है, तो आप संगतता के लिए अपने स्वयं के संस्करण को भी परिभाषित कर सकते हैं।

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

मैं <cmath> का उपयोग कर रहा था और इसमें कोई भी सम्‍मिलित नहीं है! संयोग से मुझे पता चला है कि है एक isnanमें <math.h>
Hasen

1
जैसा कि मैंने कहा, यह C99 का हिस्सा है। चूंकि C99 किसी भी वर्तमान C ++ मानक का हिस्सा नहीं है, इसलिए मैंने विकल्प प्रदान किया। लेकिन जैसा कि संभावना है कि isnan () को आगामी C ++ मानक में शामिल किया जाएगा, मैंने इसके चारों ओर #ifndef निर्देश डाला।
raimue

12

निम्न कोड NAN (सभी घातांक बिट सेट, कम से कम एक आंशिक बिट सेट) की परिभाषा का उपयोग करता है और मान लेता है कि आकार (int) = sizeof (फ्लोट) = 4. आप विवरण के लिए विकिपीडिया में NAN देख सकते हैं।

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


मेरा मानना ​​है कि यह बड़े एंडियन प्लेटफॉर्म पर भी काम करेगा। शाब्दिक 0x7fffffffबस के रूप में स्मृति में बैठेंगे ff ff ff 7fvalueके रूप में एक ही आदेश है 0x7f800000, इसलिए सभी ऑपरेशन लाइन अप (बाइट्स की कोई स्वैपिंग नहीं है)। अगर कोई इसे बड़े एंडियन प्लेटफॉर्म पर टेस्ट कर सकता है तो मुझे दिलचस्पी होगी।
ब्रायन डब्ल्यू। वैगनर

0x7fff1234एक NaN भी है। तो है0xffffffff
स्टीव हॉलैस

12

नान की रोकथाम

इस सवाल का मेरा जवाब के लिए पूर्वव्यापी जाँच का उपयोग नहीं हैnan । इसके बजाय प्रपत्र के विभाजनों के लिए निवारक जांच का उपयोग करें 0.0/0.0

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

nanऑपरेशन से परिणाम 0.f/0.f, या 0.0/0.0nanआपके कोड की स्थिरता के लिए एक भयानक नेमसिस है जिसका पता लगाया जाना चाहिए और बहुत सावधानी से रोका जाना चाहिए 1 । उस के गुण nanसामान्य संख्या से भिन्न हैं:

  • nanविषाक्त है, (5 * nan= nan)
  • nanकुछ भी नहीं के बराबर है, खुद भी नहीं nan! ( ! nan)
  • nanकिसी भी चीज़ से बड़ा नहीं ( nan> 0)
  • nanकिसी भी चीज़ से कम नहीं है nan! ( <0)

सूचीबद्ध अंतिम 2 गुण काउंटर-तार्किक हैं और कोड के विषम व्यवहार का परिणाम होगा जो एक nanसंख्या के साथ तुलना पर निर्भर करता है (तीसरी अंतिम संपत्ति भी विषम है लेकिन आप शायद कभी भी x != x ?अपने कोड में देखने के लिए नहीं जा रहे हैं (जब तक कि आप जाँच नहीं कर रहे हों नैन (अविश्वसनीय रूप से)) के लिए।

अपने स्वयं के कोड में, मैंने देखा कि nanमान कीड़े खोजने के लिए मुश्किल पैदा करते हैं। (ध्यान दें कि यह कैसे मामला नहीं है infया -inf( -inf<0) रिटर्न TRUE, (0 < inf) TRUE लौटाता है, और ( -inf< inf) TRUE भी लौटाता है। इसलिए, मेरे अनुभव में, कोड का व्यवहार अक्सर वांछित होता है)।

नैन के नीचे क्या करना है

आप 0.0/0.0 एक विशेष मामले के रूप में क्या संभालना चाहते हैं , लेकिन जो आप कोड से बाहर आने की अपेक्षा करते हैं उन पर निर्भर होना चाहिए।

उपरोक्त उदाहरण में, ( 0.f/FLT_MIN) का परिणाम 0मूल रूप से होगा। आप इसके बजाय 0.0/0.0उत्पन्न करना चाह सकते हैं HUGE। इसलिए,

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 थे 0.f, infतो परिणाम होगा (जो वास्तव में ऊपर वर्णित के रूप में बहुत अच्छा / nondestructive व्यवहार है)।

याद रखें, 0 से पूर्णांक विभाजन एक रनटाइम अपवाद का कारण बनता है । इसलिए आपको हमेशा पूर्णांक विभाजन की जांच करना चाहिए 0. केवल इसलिए कि 0.0/0.0चुपचाप मूल्यांकन करने का nanमतलब यह नहीं है कि आप आलसी हो सकते हैं और 0.0/0.0ऐसा होने से पहले जांच नहीं करते हैं।

1 के nanमाध्यम से चेक x != xकभी-कभी अविश्वसनीय होते हैं ( x != xआईईईई अनुपालन को तोड़ने वाले कुछ अनुकूलन कंपाइलरों द्वारा छीन लिया जाता है, खासकर जब -ffast-mathस्विच सक्षम होता है)।


इस पर ध्यान दिलाने के लिए धन्यवाद; उस तरह की प्रोग्रामिंग निश्चित रूप से समस्या के साथ मदद करेगी। लेकिन अगली बार, कृपया पाठ स्वरूपण सुविधाओं का बहुत अधिक दुरुपयोग न करें। फ़ॉन्ट आकार, वजन और शैली को स्विच करना जैसे कि इसे पढ़ना वास्तव में कठिन बना रहा है।
मैग्नस

4
ध्यान दें कि 0.0 / 0.0 केवल एक ऑपरेशन नहीं है जिसका परिणाम NaN हो सकता है। ऋणात्मक संख्या का वर्गमूल NaN देता है। + अनंत का कोसिन NaN भी लौटाता है। ऑपरेशन acos (x) जहां x श्रेणी में नहीं है [0, pi] भी NaN में परिणाम कर सकता है। संक्षेप में, किसी को इन संभावित जोखिम भरे कार्यों को देखने के लिए अतिरिक्त सावधान रहना होगा, न कि केवल 0.0 / 0.0 तक।
बोरिस डालस्टीन

पूरी तरह से बोरिस से सहमत हैं। मेरे अनुभव में, NaN व्यावहारिक रूप से हमेशा sqrt (-1.302e-53) जैसी चीज से आया है, अर्थात नकारात्मकता की जाँच किए बिना, क्लोज़र में खिलाए जा रहे करीब-करीब शून्य मध्यवर्ती संगणना परिणाम।
hans_meine

1
"NaNs को रोकना" का अर्थ है कि आपको केवल विभाजन नहीं बल्कि सभी बुनियादी अंकगणितीय कार्यों के अंदर जाने की आवश्यकता है। आपको। / ∞, 0 * ∞,,% x, x% 0, ∞ - 0, 0 ^ 0, ∞ ^ 0, कई अन्य लोगों के लिए घड़ी देखने की आवश्यकता होगी। इस तरह के बुनियादी अंकगणितीय कार्यों के साथ "निवारक" होने का मतलब है कि आप अपने प्रदर्शन को पूरी तरह से टैंक कर लेंगे (और संभवतः उन अतिरिक्त मामलों को याद करें जिन्हें आपने नहीं सोचा था)।
स्टीव हॉलैस

11

C ++ 14 के अनुसार परीक्षण के कई तरीके हैं यदि फ्लोटिंग पॉइंट नंबर valueNaN है।

इन तरीकों में से, केवल संख्या के प्रतिनिधित्व के बिट्स की जांच करना , मज़बूती से काम करता है, जैसा कि मेरे मूल उत्तर में उल्लेखित है। विशेष रूप से, std::isnanऔर अक्सर प्रस्तावित चेक v != v, मज़बूती से काम नहीं करते हैं और इसका उपयोग नहीं किया जाना चाहिए, ऐसा न हो कि आपके कोड सही ढंग से काम करना बंद कर दें जब कोई तय करता है कि फ्लोटिंग पॉइंट ऑप्टिमाइज़ेशन की आवश्यकता है, और कंपाइलर को ऐसा करने के लिए कहता है। यह स्थिति बदल सकती है, संकलक अधिक अनुरूप हो सकते हैं, लेकिन इस मुद्दे के लिए जो मूल उत्तर के बाद से 6 वर्षों में नहीं हुआ है।

लगभग 6 वर्षों तक मेरा मूल उत्तर इस प्रश्न के लिए चयनित समाधान था, जो ठीक था। लेकिन हाल ही में अविश्वसनीय v != vपरीक्षण की सिफारिश करने वाले एक अत्यधिक उत्क्रमित उत्तर का चयन किया गया है। इसलिए यह अतिरिक्त अप-टू-डेट उत्तर (हमारे पास अब C ++ 11 और C ++ 14 मानक हैं, और क्षितिज पर C ++ 17)।


C ++ 14 के अनुसार NaN-ness के लिए जाँच करने के मुख्य तरीके हैं:

  • std::isnan(value) )
    C ++ 11 के बाद से इरादा मानक पुस्तकालय तरीका है। isnanजाहिरा तौर पर एक ही नाम के Posix मैक्रो के साथ संघर्ष, लेकिन व्यवहार में एक समस्या नहीं है। मुख्य समस्या यह है कि जब फ़्लोटिंग पॉइंट अंकगणितीय अनुकूलन का अनुरोध किया जाता है, तो कम से कम एक मुख्य संकलक, अर्थात् g ++ के साथ, NaN तर्क के लिए std::isnan लौटता falseहै

  • (fpclassify(value) == FP_NAN) )
    एक ही समस्या से पीड़ित के रूप में std::isnan, यानी, विश्वसनीय नहीं है।

  • (value != value) )
    कई SO उत्तरों में अनुशंसित। एक ही समस्या से पीड़ित के रूप में std::isnan, यानी, विश्वसनीय नहीं है।

  • (value == Fp_info::quiet_NaN()) )
    यह एक ऐसा परीक्षण है जिसमें मानक व्यवहार के साथ NaN का पता नहीं लगाना चाहिए, लेकिन यह कि अनुकूलित व्यवहार के साथ शायद NaN का पता लगा सकते हैं (अनुकूलित कोड के कारण सीधे बिटलेवल निरूपण की तुलना कर सकते हैं), और शायद मानक अन-अनुकूलित व्यवहार को कवर करने के लिए किसी अन्य तरीके के साथ संयुक्त। , दृढ़ता से NaN का पता लगा सकता है। दुर्भाग्य से यह मज़बूती से काम नहीं करने के लिए निकला।

  • (ilogb(value) == FP_ILOGBNAN) )
    एक ही समस्या से पीड़ित के रूप में std::isnan, यानी, विश्वसनीय नहीं है।

  • isunordered(1.2345, value) )
    एक ही समस्या से पीड़ित के रूप में std::isnan, यानी, विश्वसनीय नहीं है।

  • is_ieee754_nan( value ) )
    यह एक मानक कार्य नहीं है। यह IEEE 754 मानक के अनुसार बिट्स की जाँच कर रहा है। यह पूरी तरह से विश्वसनीय है लेकिन कोड कुछ हद तक प्रणाली पर निर्भर है।


निम्नलिखित पूर्ण परीक्षण कोड में "सफलता" है कि क्या एक अभिव्यक्ति मूल्य के नेन-नेस की रिपोर्ट करती है। अधिकांश अभिव्यक्तियों के लिए सफलता का यह उपाय, NaN और केवल NaN का पता लगाने का लक्ष्य, उनके मानक शब्दार्थ से मेल खाता है। के लिए (value == Fp_info::quiet_NaN()) )अभिव्यक्ति, तथापि, मानक व्यवहार है कि यह एक NaN-डिटेक्टर के रूप में काम नहीं करता है।

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

जी ++ के साथ परिणाम (ध्यान दें कि फिर से मानक व्यवहार (value == Fp_info::quiet_NaN()) यह है कि यह NaN-डिटेक्टर के रूप में काम नहीं करता है, यह सिर्फ व्यावहारिक हित के बहुत अधिक है):

[C: \ my \ forum \ so \ 282 (NaN का पता लगाएं)]
> g ++ --version | "++" ढूंढें
g ++ (x86_64-win32-sjlj-Rev1, मिनगॉ-डब्ल्यू 64 परियोजना द्वारा निर्मित) 6.3.0

[C: \ my \ forum \ so \ 282 (NaN का पता लगाएं)]
> g ++ foo.cpp && a
कंपाइलर IEEE 754 = सत्य का दावा करता है

v = nan, (std :: isnan (value)) = सच्ची सफलता
u = 3.14, (std :: isnan (value)) = झूठी सफलता
w = inf, (std :: isnan (value)) = झूठी सफलता

v = nan, ((fpclassify (मान) == 0x0100)) = सच्ची सफलता
u = 3.14, ((fpclassify (मान) == 0x0100)) = झूठी सफलता
w = inf, ((fpclassify (value) == 0x0100)) = false सफलता

v = nan, ((value! = value) = true सफलता
u = 3.14, (मान! = मान) = झूठी सफलता
w = inf, ((value! = value) = false सफलता

v = nan, ((value == Fp_info :: शांत_NaN ())) = झूठी विफल
u = 3.14, (मान == Fp_info :: शांत_NaN ())) = झूठी सफलता
w = inf, ((value == Fp_info :: शांत_NaN ())) = झूठी सफलता

v = nan, ((ilogb (value) == ((int) 0x80000000)) = true सफलता
u = 3.14, ((ilogb (value) == ((int) 0x80000000))) = झूठी सफलता
w = inf, ((ilogb (value) == ((int) 0x80000000)) = false सफलता

v = nan, (isunordered (1.2345, मान)) = सच्ची सफलता
u = 3.14, (isunordered (1.2345, मान)) = झूठी सफलता
w = inf, (isunordered (1.2345, मान)) = झूठी सफलता

v = nan, (is_ieee754_nan (मान)) = सच्ची सफलता
u = 3.14, (is_ieee754_nan (मान)) = झूठी सफलता
w = inf, (is_ieee754_nan (मान)) = झूठी सफलता

[C: \ my \ forum \ so \ 282 (NaN का पता लगाएं)]
> g ++ foo.cpp -ffast-math && a
कंपाइलर IEEE 754 = सत्य का दावा करता है

v = nan, (std :: isnan (value)) = false FAILED
u = 3.14, (std :: isnan (value)) = झूठी सफलता
w = inf, (std :: isnan (value)) = झूठी सफलता

v = nan, ((fpclassify (value) == 0x0100)) = false FAILED
u = 3.14, ((fpclassify (मान) == 0x0100)) = झूठी सफलता
w = inf, ((fpclassify (value) == 0x0100)) = false सफलता

v = nan, ((value! = value) = false FAILED
u = 3.14, (मान! = मान) = झूठी सफलता
w = inf, ((value! = value) = false सफलता

v = nan, ((value == Fp_info :: शांत_NaN ())) = सच्ची सफलता
u = 3.14, (मान == Fp_info :: शांत_NaN ())) = सच विफल
w = inf, ((value == Fp_info :: शांत_NaN ())) = सच विफल

v = nan, ((ilogb (value) == ((int) 0x80000000)) = true सफलता
u = 3.14, ((ilogb (value) == ((int) 0x80000000))) = झूठी सफलता
w = inf, ((ilogb (value) == ((int) 0x80000000)) = false सफलता

v = nan, (isunordered (1.2345, मान)) = झूठी विफल
u = 3.14, (isunordered (1.2345, मान)) = झूठी सफलता
w = inf, (isunordered (1.2345, मान)) = झूठी सफलता

v = nan, (is_ieee754_nan (मान)) = सच्ची सफलता
u = 3.14, (is_ieee754_nan (मान)) = झूठी सफलता
w = inf, (is_ieee754_nan (मान)) = झूठी सफलता

[C: \ my \ forum \ so \ 282 (NaN का पता लगाएं)]
> _

दृश्य C ++ के परिणाम:

[C: \ my \ forum \ so \ 282 (NaN का पता लगाएं)]
> cl / nologo- 2> & 1 | "++" ढूंढें
Microsoft (R) C / C ++ का कंपाइलिंग वर्जन 19.00.23725 x86 के लिए

[C: \ my \ forum \ so \ 282 (NaN का पता लगाएं)]
> cl foo.cpp / Feb && b
foo.cpp
कंपाइलर IEEE 754 = सत्य का दावा करता है

v = nan, (std :: isnan (value)) = सच्ची सफलता
u = 3.14, (std :: isnan (value)) = झूठी सफलता
w = inf, (std :: isnan (value)) = झूठी सफलता

v = nan, ((fpclassify (value) == 2)) = true सफलता
u = 3.14, (fpclassify (value) == 2)) = false सफलता
w = inf, ((fpclassify (value) == 2) = झूठी सफलता

v = nan, ((value! = value) = true सफलता
u = 3.14, (मान! = मान) = झूठी सफलता
w = inf, ((value! = value) = false सफलता

v = nan, ((value == Fp_info :: शांत_NaN ())) = झूठी विफल
u = 3.14, (मान == Fp_info :: शांत_NaN ())) = झूठी सफलता
w = inf, ((value == Fp_info :: शांत_NaN ())) = झूठी सफलता

v = nan, ((ilogb (value) == 0x7fffffff)) = सच्ची सफलता
u = 3.14, (ilogb (मान) == 0x7fffffff)) = झूठी सफलता
w = inf, ((ilogb (value) == 0x7fffffff)) = true FAILED

v = nan, (isunordered (1.2345, मान)) = सच्ची सफलता
u = 3.14, (isunordered (1.2345, मान)) = झूठी सफलता
w = inf, (isunordered (1.2345, मान)) = झूठी सफलता

v = nan, (is_ieee754_nan (मान)) = सच्ची सफलता
u = 3.14, (is_ieee754_nan (मान)) = झूठी सफलता
w = inf, (is_ieee754_nan (मान)) = झूठी सफलता

[C: \ my \ forum \ so \ 282 (NaN का पता लगाएं)]
> सीएल foo.cpp / Feb / fp: fast && b
foo.cpp
कंपाइलर IEEE 754 = सत्य का दावा करता है

v = nan, (std :: isnan (value)) = सच्ची सफलता
u = 3.14, (std :: isnan (value)) = झूठी सफलता
w = inf, (std :: isnan (value)) = झूठी सफलता

v = nan, ((fpclassify (value) == 2)) = true सफलता
u = 3.14, (fpclassify (value) == 2)) = false सफलता
w = inf, ((fpclassify (value) == 2) = झूठी सफलता

v = nan, ((value! = value) = true सफलता
u = 3.14, (मान! = मान) = झूठी सफलता
w = inf, ((value! = value) = false सफलता

v = nan, ((value == Fp_info :: शांत_NaN ())) = झूठी विफल
u = 3.14, (मान == Fp_info :: शांत_NaN ())) = झूठी सफलता
w = inf, ((value == Fp_info :: शांत_NaN ())) = झूठी सफलता

v = nan, ((ilogb (value) == 0x7fffffff)) = सच्ची सफलता
u = 3.14, (ilogb (मान) == 0x7fffffff)) = झूठी सफलता
w = inf, ((ilogb (value) == 0x7fffffff)) = true FAILED

v = nan, (isunordered (1.2345, मान)) = सच्ची सफलता
u = 3.14, (isunordered (1.2345, मान)) = झूठी सफलता
w = inf, (isunordered (1.2345, मान)) = झूठी सफलता

v = nan, (is_ieee754_nan (मान)) = सच्ची सफलता
u = 3.14, (is_ieee754_nan (मान)) = झूठी सफलता
w = inf, (is_ieee754_nan (मान)) = झूठी सफलता

[C: \ my \ forum \ so \ 282 (NaN का पता लगाएं)]
> _

उपरोक्त परिणामों को सारांशित करते हुए, is_ieee754_nanइस परीक्षण कार्यक्रम में परिभाषित फ़ंक्शन का उपयोग करते हुए, बिट-स्तरीय प्रतिनिधित्व का केवल प्रत्यक्ष परीक्षण, जी + और विज़ुअल सी ++ दोनों के साथ सभी मामलों में मज़बूती से काम करता है।


परिशिष्ट:
उपरोक्त पोस्ट करने के बाद मैं NaN के लिए परीक्षण करने के लिए अभी तक एक और संभावित के बारे में अवगत हो गया, जिसका उल्लेख यहां एक अन्य उत्तर में किया गया है ((value < 0) == (value >= 0))। यह दृश्य C ++ के साथ ठीक काम करने के लिए निकला, लेकिन g ++ के -ffast-mathविकल्प के साथ विफल रहा । केवल प्रत्यक्ष बिटपैटन परीक्षण मज़बूती से काम करता है।


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

यह काम करता है यदि sizeof(int)4 है और sizeof(long long)8 है।

रन टाइम के दौरान यह केवल तुलना है, कास्टिंग में कोई समय नहीं लगता है। यह समानता की जांच करने के लिए तुलनात्मक झंडे विन्यास को बदलता है।


यह भी ध्यान दें, यह IEEE 754 प्रतिनिधित्व तक सीमित है।
चीयर्स एंड हीथ। - अल्फ

ध्यान दें कि यह कलाकार जी ++ के सख्त अलियासिंग नियम को तोड़ता है, और जब यह औपचारिक यूबी का पता लगाता है, तो कंपाइलर को अनिमेन्सेबल थिंग्स ™ करने के लिए जाना जाता है। कुशल कलाकारों के बजाय, जी ++ के साथ आपको memcpyएक बाइट सरणी के माध्यम से उपयोग करने की आवश्यकता है , यह सुनिश्चित करने के लिए। मेरे # 2 उत्तर में उसके लिए कोड
चीयर्स एंड हीथ। - अल्फ

4

एक संभावित समाधान जो NaN के लिए विशिष्ट IEEE प्रतिनिधित्व पर निर्भर नहीं करेगा, वह निम्नलिखित होगा:

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

एकल-सटीक फ़्लोटिंग पॉइंट में NaN के लिए 8 मिलियन से अधिक वैध और अलग-अलग बिट रिप्रेसेनशन हैं, इसलिए आपको कुछ और तुलनाओं को जोड़ना होगा। :)
स्टीव हॉलैस

4

यह देखते हुए कि (x! = X) हमेशा NaN के लिए गारंटी नहीं दी जाती है (जैसे कि अगर -ffast-math विकल्प का उपयोग करके), तो मैं उपयोग कर रहा हूं:

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

नंबर <0 और> = 0 दोनों नहीं हो सकते हैं, इसलिए वास्तव में यह चेक केवल पास होता है यदि संख्या न तो से कम है, न ही शून्य से अधिक है या बराबर है। जो मूल रूप से कोई संख्या नहीं है, या NaN नहीं है।

यदि आप चाहें तो आप इसका उपयोग भी कर सकते हैं:

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

मुझे यकीन नहीं है कि यह -फास्ट-गणित से कैसे प्रभावित होता है, इसलिए आपका माइलेज अलग-अलग हो सकता है।


यह वास्तव में त्रुटिपूर्ण है उसी तरह f != fत्रुटिपूर्ण है। मैंने llvm को कोड के लगभग समान टुकड़े को अनुकूलित करते हुए देखा है। ऑप्टिमाइज़र पहली तुलना के बारे में जानकारी को प्रचारित कर सकता है और यह पता लगा सकता है कि यदि पहला है तो दूसरा कंप्रेशन कभी भी सही नहीं हो सकता। (यदि कंपाइलर कड़ाई से आईईईई के नियमों f != fका पालन ​​करता है, तो बहुत आसान है)
मार्कस

जी ++ के -ffast-mathविकल्प के साथ काम नहीं करता है । विजुअल C ++ के साथ काम करता है। देखें ( stackoverflow.com/a/42138465/464581 )।
चीयर्स एंड हीथ। - अल्फ

3

मेरे लिए समाधान स्पष्ट रूप से इनलाइन बनाने के लिए एक मैक्रो हो सकता है और इस तरह पर्याप्त तेजी से हो सकता है। यह किसी भी फ्लोट प्रकार के लिए भी काम करता है। यह इस तथ्य पर आधारित है कि एकमात्र मामला जब कोई मूल्य स्वयं के बराबर नहीं होता है जब मूल्य एक संख्या नहीं होती है।

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

यह इस सवाल का सबसे अच्छा जवाब में से एक है! साझा करने के लिए धन्यवाद।
हेनरी मेंके

2
अन्य उत्तर इंगित करते हैं कि यह -ffast-math विकल्प सेट के साथ विफल हो सकता है।
टेक्नोफाइल

3

यह काम:

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

आउटपुट: आइसन


1

यह मुझे लगता है कि सबसे सही मायने में क्रॉस-प्लेटफ़ॉर्म दृष्टिकोण एक संघ का उपयोग करना और एनएएन के लिए जाँच करने के लिए डबल पैटर्न के बिट पैटर्न का परीक्षण करना होगा।

मैंने इस समाधान का पूरी तरह से परीक्षण नहीं किया है, और बिट पैटर्न के साथ काम करने का एक अधिक कुशल तरीका हो सकता है, लेकिन मुझे लगता है कि यह काम करना चाहिए।

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

ध्यान दें कि "यह संघ के उस सदस्य से पढ़ने के लिए अपरिभाषित व्यवहार है जो हाल ही में नहीं लिखा गया था"। अतः unionदो प्रकारों के बीच एक से टाइप-सज़ा का यह प्रयोग वांछित के रूप में काम नहीं कर सकता है: (दुख की बात है :)। सही (यद्यपि वास्तव में वांछित के रूप में काफी पोर्टेबल नहीं है) संघ से बचने के लिए पूरी तरह से होगा, और doubleएक अलग uint64_tचर में एक ज्ञापन करते हैं , तो उस सहायक चर का उपयोग करके परीक्षण करें।
एलजय

0

X86-64 पर आपके पास NaN और अनंत के लिए जाँच करने के लिए बहुत तेज़ तरीके हो सकते हैं, जो -ffast-mathसंकलक विकल्प की परवाह किए बिना काम करते हैं । ( f != f, हमेशा std::isnan, साथ std::isinfउपज )।false-ffast-math


NaN, अनंत और परिमित संख्याओं के लिए परीक्षण आसानी से अधिकतम प्रतिपादक के लिए जाँच करके किया जा सकता है। अनंत शून्य मंटिसा के साथ अधिकतम प्रतिपादक है, NaN अधिकतम प्रतिपादक और गैर-शून्य मंटिसा है। घातांक को सबसे ऊपरी साइन बिट के बाद अगले बिट्स में संग्रहित किया जाता है, ताकि हम साइन बिट से छुटकारा पाने के लिए शिफ्ट को छोड़ सकें और घातांक को सबसे ऊपरी बिट्स बना सकें, कोई मास्किंग ( operator&) आवश्यक नहीं है:

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

stdके संस्करणों isinfऔर isfiniteलोड 2 double/floatसे स्थिरांक .dataखंड और सबसे खराब स्थिति में वे 2 डेटा कैश छूट जाए पैदा कर सकता है। उपरोक्त संस्करण किसी भी डेटा को लोड नहीं करते हैं, inf_double_shl1और inf_float_shl1असेंबली तत्काल निर्देश के रूप में विधानसभा निर्देशों में संलग्न हो जाती है।


तेज़ isnan2सिर्फ 2 विधानसभा निर्देश हैं:

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

इस तथ्य का उपयोग करता है कि ucomisdयदि कोई तर्क NaN है तो अनुदेश समता ध्वज को सेट करता है। std::isnanजब कोई -ffast-mathविकल्प निर्दिष्ट नहीं किया जाता है तो यह कैसे काम करता है।


-1

IEEE मानक कहता है कि जब घातांक सभी 1s है और mantissa शून्य नहीं है, तो संख्या a है NaN। डबल 1साइन बिट, 11एक्सपोनेंट बिट्स और 52मंटिसा बिट्स है। थोड़ा जांच-परख कर लें।


-3

जैसा कि राज्य के ऊपर टिप्पणी =! = G ++ और कुछ अन्य संकलक में काम नहीं करेगा, लेकिन यह चाल होनी चाहिए। यह उतना कुशल नहीं हो सकता है, लेकिन यह अभी भी एक तरीका है:

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

मूल रूप से, जी ++ में (हालांकि मैं दूसरों के बारे में निश्चित नहीं हूं) अगर प्रिंट वेरिएबल एक वैध पूर्णांक / फ्लोट नहीं है, तो% d या% .f फॉर्मेट पर प्रिंटफ नेन प्रिंट करता है। इसलिए यह कोड 'n' होने के लिए स्ट्रिंग के पहले वर्ण के लिए जाँच कर रहा है (जैसा कि "nan")


2
यदि एक = 234324.0f एक बफर अतिप्रवाह का कारण नहीं होगा?
Mazyod

हाँ t'will, या 340282346638528859811704183484516925440.000यदि a = FLT_MAX। वह उपयोग करना होगा char s[7]; sprintf(s, "%.0g", a);, जो 6 chrs होगा अगर a=-FLT_MAX, या-3e+38
bobobobo

-3

यह दृश्य स्टूडियो में अनंत और भी NaN का पता लगाता है, इसकी दोहरी सीमा के भीतर:

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

की परिभाषा की जाँच करें FLT_MIN, DBL_MINऔर LDBL_MINअधिक ध्यान से। इन्हें प्रत्येक प्रकार के लिए सबसे छोटा सामान्यीकृत मान माना जाता है । उदाहरण के लिए, एकल-सटीक में 8 मिलियन से अधिक वैध मूल्य मान हैं जो शून्य से अधिक हैं और FLT_MIN(और NaN से कम नहीं) हैं।
स्टीव हॉलश
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.