التحقق مما إذا كان مزدوج (أو تعويم) نان في C ++

StackOverflow https://stackoverflow.com/questions/570669

  •  05-09-2019
  •  | 
  •  

سؤال

هل هناك وظيفة Isnan ()؟

ملاحظة: أنا في مينغو (إذا كان هذا يحدث فرقا).

كان لدي هذا الحل باستخدام Isnan () من <math.h>, ، والتي غير موجودة في <cmath>, ، الذي كنت #includeجي في البداية.

هل كانت مفيدة؟

المحلول

وفقا لمعيار IEEE، فإن قيم نان لديها الخاصية الفردية التي تقارنات التي تنطوي عليها دائما خاطئة. وهذا هو، لتطفو f، f != f سيكون صحيحا فقط إذا كانت و نان.

لاحظ أنه، كما أشارت بعض التعليقات أدناه، وليس جميع المترجمين يحترمون هذا عند تحسين التعليمات البرمجية.

لأي مترجم يدعي استخدام النقطة العائمة IEEE، هذه الخدعة ينبغي الشغل. لكنني لا أستطيع ضمان ذلك إرادة العمل في الممارسة العملية. تحقق مع المترجم الخاص بك، إذا كنت في شك.

نصائح أخرى

لا يوجد isnan() الوظيفة المتاحة في مكتبة C ++ القياسية الحالية. تم تقديمه في C99. وتحديد كما دقيق ليس وظيفة. لا تعد عناصر المكتبة القياسية المحددة من قبل C99 جزءا من ISO / IEC 14882: 1998 لا يتحدث ISO / IEC 14882: 2003.

في عام 2005، تم اقتراح التقرير الفني الأول. يجلب TR1 التوافق مع C99 إلى C ++. على الرغم من حقيقة أنه لم يتم اعتماده رسميا ليصبح معيار C ++، كثير (دول مجلس التعاون الخليجي 4.0+. أو Visual C ++ 9.0+ تقوم تطبيقات C ++ بتزويد ميزات TR1، وكلها أو بعضها فقط (Visual C ++ 9.0 لا يوفر وظائف الرياضيات C99).

إذا كان TR1 متاح، ثم cmath يشمل عناصر C99 مثل isnan(), isfinite(), ، وما إلى ذلك ولكن يتم تعريفها كوظائف، وليس وحدات ماكرو، عادة في std::tr1:: مساحة الاسم، على الرغم من أن العديد من التطبيقات (أي دول مجلس التعاون الخليجي 4+ على Linux أو في Xcode على Mac OS X 10.5+) حقنها مباشرة std::, ، وبالتالي std::isnan محددة جيدا.

علاوة على ذلك، فإن بعض تطبيقات C ++ لا تزال تجعل C99 isnan() ماكرو متاح ل C ++ (المدرجة من خلال cmath أو math.h)، ما قد يسبب المزيد من التشكيك والمطورين قد يفترض أنه سلوك قياسي.

ملاحظة حول Viusal C ++، كما هو مذكور أعلاه، فإنه لا يوفر std::isnan لا هذا ولا ذاك std::tr1::isnan, ، لكنه يوفر وظيفة تمديد يعرف باسم _isnan() التي كانت متاحة منذ Visual C ++ 6.0

على xcode، هناك أكثر متعة. كما ذكر، يحدد دول مجلس التعاون الخليجي 4+ std::isnan. وبعد بالنسبة للإصدارات القديمة من مترجم ومكتبة نموذج XCode، يبدو (هنا المناقشة ذات الصلة)، لم تتحدث فرصة للتحقق من نفسي) يتم تعريف وظيفتين، __inline_isnand() على إنتل و __isnand() على Power PC.

الحل الأول: إذا كنت تستخدم C ++ 11

منذ أن طلب هذا، كان هناك بعض التطورات الجديدة: من المهم أن تعرف ذلك std::isnan() هو جزء من C ++ 11

ملخص

محددة في الرأس <cmath>

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

يحدد ما إذا كان ALG رقم النقطة العائمة المعطاة غير رقم (NaN).

حدود

arg: قيمة النقطة العائمة

قيمة الإرجاع

true إذا arg هو NaN, false غير ذلك

مرجع

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

يرجى ملاحظة أن هذا غير متوافق مع الرياضيات -ast إذا كنت تستخدم G ++، انظر أدناه للحصول على اقتراحات أخرى.


حلول أخرى: إذا كنت تستخدم أدوات متوافقة مع NON C ++ 11

بالنسبة لل C99، في ج، يتم تنفيذ هذا كجهاز كورو isnan(c)التي ترجع قيمة int. نوع من x يجب أن تكون تعويم، مزدوجة أو طويلة مزدوجة.

قد يشمل أو لا تشمل العديد من البائعين أو لا يعملون isnan().

الطريقة المحمولة المفترض للتحقق من NaN هو استخدام خاصية IEEE 754 التي NaN لا يساوي نفسه: أي x == x سوف تكون خاطئة x مستخدم NaN.

ومع ذلك، قد لا يعمل الخيار الأخير مع كل مترجم وبعض الإعدادات (خاصة إعدادات التحسين)، لذلك في الملاذ الأخير، يمكنك دائما التحقق من نمط بت ...

هنالك أيضا مكتبة الرأس فقط موجودة في دفعة تحتوي على أدوات أنيقة للتعامل مع أنواع البيانات العائمة

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

إذا كان لديك وقت، فبعد إلقاء نظرة على مجموعة أدوات الرياضيات بأكملها من دفعة، فإن لديها العديد من الأدوات المفيدة وتنمو بسرعة.

أيضا عند التعامل مع النقاط العائمة وغير العائمة قد تكون فكرة جيدة أن ننظر إلى التحويلات الرقمية.

هناك ثلاثة طرق "رسمية": Posix isnan دقيق, ، C ++ 0x isnan قالب وظيفة, أو Visual C ++ _isnan وظيفة.

لسوء الحظ، من غير العملي للغاية اكتشاف أي من أولئك الذين يستخدمون استخدامها.

ولسوء الحظ، لا توجد طريقة موثوقة للكشف عن ما إذا كان لديك تمثيل IEEE 754 مع NANS. توفر المكتبة القياسية طريقة رسمية (numeric_limits<double>::is_iec559). ولكن في الممارسة المبرمجة مثل G ++ برغي ذلك.

من الناحية النظرية يمكن للمرء استخدام ببساطة x != x, ، ولكن مترجم مثل G ++ و Visual C ++ برغي ذلك.

لذلك في النهاية، اختبار محدد نان bitpatterns., ، على افتراض (ونأمل أن تنفذ، في مرحلة ما!) تمثيل معين مثل IEEE 754.


تعديل: كمثال على "محامصات مثل G ++ ... المسمار هذا حتى"، فكر

#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) 4.4.1:

C:  TEST> اكتب "C:  Program Files commands  gnuc.bat" REM -FINPUT-Charset = Windows-1252 @ G ++ -O -Ppedant = C ++ 98 -Wall -Write-Strings٪ * -WN -WN-Long-Long C:  TEST> GNUC X.CPP C:  Test> W && Echo Works ... || صدى! فشل العمل ... C:  Test> GNUC X.CPP - Fast Math C:  Test> W && Echo Works ... || ECHO! فشل تأكيد فشل: a! = b، file x.cpp، السطر 6، طلب هذا التطبيق وقت التشغيل لإنهاءه بطريقة غير عادية. يرجى الاتصال بفريق دعم التطبيقات للحصول على مزيد من المعلومات. أخفق ج:  Test> _

هناك STD :: ISNAN إذا كنت يدعم برنامج التحويل البرمجي ملحقات C99، لكنني لست متأكدا مما إذا كان MINGW يفعل.

فيما يلي وظيفة صغيرة يجب أن تعمل إذا لم يكن لدى مترجمك الوظيفة القياسية:

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

يمكنك استخدام 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;
}

لا أعرف إذا كان هذا يعمل على جميع المنصات، لأنني اختبرت فقط مع G ++ على Linux.

يمكنك استعمال ال isnan() وظيفة، ولكن تحتاج إلى تضمين مكتبة الرياضيات C.

#include <cmath>

نظرا لأن هذه الوظيفة جزء من C99، فهي غير متوفرة في كل مكان. إذا كان البائع الخاص بك لا يوفر الوظيفة، فيمكنك أيضا تحديد البديل الخاص بك للتوافق.

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

منع نان

إجابتي على هذا السؤال هو لا تستخدم الشيكات بأثر رجعي 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.0. nan هو عدو للغاية لاستقرار التعليمات البرمجية التي يجب اكتشافها و منع حذر جدا1. وبعد خصائص nan التي تختلف عن الأرقام العادية:

  • nan هو سام، (5 *nan=nan)
  • nan لا يساوي أي شيء، ولا حتى في حد ذاته (nan != nan)
  • nan ليست أكبر من أي شيء (nan !> 0)
  • nan ليس أقل من أي شيء (nan !< 0)

آخر 2 الخصائص المدرجة هي المنطقية المضادة وستؤدي إلى سلوك غريب من التعليمات البرمجية تعتمد على مقارنات مع nan عدد (الخاصية الثالثة الأخيرة أمر غريب أيضا ولكن ربما لن ترى أبدا x != x ? في التعليمات البرمجية الخاصة بك (إلا إذا كنت تقوم بالتحقق من NAN (غير موثوق بها))).

في الرمز الخاص بي، لاحظت ذلك nan القيم تميل إلى إنتاج صعوبة في العثور على الأخطاء. (لاحظ كيف هذا ليس القضية لاجل inf أو -inf. (-inf <0) إرجاع TRUE, ( 0 < inf ) إرجاع صحيح وحتى (-inf < inf) إرجاع صحيح. لذلك، في تجربتي، سلوك الكود غالبا لا يزال حسب الرغبة).

ماذا تفعل تحت نان

ما تريد أن يحدث تحت 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 سيؤدي (الذي يحتوي على سلوك جيد / غير مدروس على النحو المذكور أعلاه بالفعل).

تذكر، يسبب قسم عدد صحيح بواسطة 0 استثناء وقت التشغيل. وبعد لذلك يجب عليك دائما التحقق من وجود قسم عدد صحيح بواسطة 0. لمجرد 0.0/0.0 يقيم بهدوء إلى nan لا يعني أنه يمكنك أن تكون كسول ولا تحقق من 0.0/0.0 قبل أن يحدث ذلك.

1 الشيكات ل nan عبر x != x هي في بعض الأحيان غير موثوق بها (x != x يجري تجريدها من قبل بعض المحاصيل التحسين التي تنكسر امتثال IEEE، وتحديدا عند -ffast-math يتم تمكين التبديل).

يستخدم التعليمة البرمجية التالية تعريف NAN (جميع مجموعة البتات المتأرة، ومجموعة بت جزء صغير على الأقل) ويفترض أن SizeOf (int) = Sizeof (Float) = 4. يمكنك البحث عن نان في ويكيبيديا للحصول على التفاصيل.

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

اعتبارا من C ++ 14 هناك عدد من الطرق لاختبار ما إذا كان رقم نقطة عائمة value هي نان.

من هذه الطرق فقط فحص البتات من تمثيل الرقم، يعمل بشكل موثوق، كما لوحظ في إجابتي الأصلية. خاصه، std::isnan والشيك المقترح في كثير من الأحيان v != v, لا تعمل بشكل موثوق ولا ينبغي استخدامها، خشية توقف التعليمات البرمجية عن العمل بشكل صحيح عندما يقرر شخص ما أن الأمثل الفاصلة العائمة مطلوبة، ويسأل المحول البرمجي القيام بذلك. يمكن أن يتغير هذا الوضع، يمكن للمجمعات المبراءات الحصول على مزيد من المطابقة، ولكن بالنسبة لهذه المسألة التي لم تحدث في 6 سنوات منذ الإجابة الأصلية.

لحوالي 6 سنوات، كانت إجابتي الأصلية الحل المحدد لهذا السؤال، وهو ما يرابط. ولكن في الآونة الأخيرة إجابة شديدة الاختتام بالتوصية غير موثوق بها v != v تم اختيار الاختبار. وبالتالي، فإن هذا الإجابة الإضافية الحديثة الإضافية (لدينا الآن معايير C ++ 11 و C ++ 14، و C ++ 17 في الأفق).


الطرق الرئيسية للتحقق من نان نيس، اعتبارا من C ++ 14، هي:

  • std::isnan(value) )
    هي طريقة المكتبة القياسية المقصودة منذ C ++ 11. isnan على ما يبدو يتعارض مع ماكرو Posix من نفس الاسم، ولكن في الممارسة العملية ليست مشكلة. المشكلة الرئيسية هي أنه عند طلب التحسيم الحسابي النقطة العائمة، ثم مع مترجم رئيسي على الأقل، أي G ++، std::isnan عائدات false وسيطة نان.

  • (fpclassify(value) == FP_NAN) )
    يعاني من نفس المشكلة std::isnan, ، أي غير موثوق.

  • (value != value) )
    الموصى بها في العديد من الإجابات. يعاني من نفس المشكلة std::isnan, ، أي غير موثوق.

  • (value == Fp_info::quiet_NaN()) )
    هذا اختبار أنه مع السلوك القياسي لا ينبغي أن يكتشف NANS، ولكن مع السلوك الأمثل ربما يمكن أن يكتشف nans (بسبب الرمز الأمثل لم يقرص فقط على تمثيلات القبطة مباشرة)، وربما مع طريقة أخرى لتغطية السلوك المعياري غير المحسن ، يمكن أن يكتشف نان بشكل موثوق. لسوء الحظ، اتضح أنه لا يعمل بشكل موثوق.

  • (ilogb(value) == FP_ILOGBNAN) )
    يعاني من نفس المشكلة std::isnan, ، أي غير موثوق.

  • isunordered(1.2345, value) )
    يعاني من نفس المشكلة std::isnan, ، أي غير موثوق.

  • is_ieee754_nan( value ) )
    هذه ليست وظيفة قياسية. إنه يتحقق من البتات وفقا لمعيار IEEE 754. انها موثوقة تماما لكن الرمز يعتمد على نظام إلى حد ما.


في رمز الاختبار الكامل التالي "النجاح" هو ما إذا كان التعبير تقارير نان نيس من القيمة. بالنسبة لمعظم التعبيرات هذا مقياس النجاح، فإن هدف اكتشاف NANS و NANS فقط، يتوافق مع دلالاتهم القياسية. ل (value == Fp_info::quiet_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 ) );
}

النتائج مع g ++ (ملاحظة مرة أخرى أن السلوك القياسي لل (value == Fp_info::quiet_NaN()) هو أنه لا يعمل كاشتراك نان، إنه فقط الكثير من الاهتمام العملي هنا)

C:  My  Forums  So  282 (كشف نان)]> G ++ - -version | البحث عن "++"G ++ (X86_64-Win32-SJLJ-REV1، بنيت بواسطة مشروع MINGW-W64) 6.3.0 [C:  My  My Forums  So  282 (كشف NAN)]> g ++ foo.cpp && aمترجم يدعي IEEE 754 = TRUE V = NAN، (STD :: ISNAN (القيمة)) = النجاح الحقيقي U = 3.14، (STD :: ISNAN (القيمة)) = النجاح الخاطئ W = INF، (STD :: ISNAN (القيمة) ) = النجاح الخاطئ v = nan، ((fpclassify (القيمة) == 0x0100)) = النجاح الحقيقي u = 3.14، ((fpclassify (القيمة) == 0x0100)) = نجاح خاطئ W = INF، ((fpclassify (القيمة) == 0x0100)) = نجاح خاطئ V = NAN، ((القيمة! = القيمة)) = النجاح الحقيقي U = 3.14، ((القيمة! = القيمة)) = نجاح خاطئ W = INF، ((القيمة! = القيمة)) = النجاح الخاطئ V = NAN، ((القيمة == FP_INFO :: CONEY_NAN ()) = خطأ FALSE U = 3.14، ((القيمة == FP_INFO :: Quiet_NAN ()) = نجاح كاذب W = INF، ((القيمة == FP_INFO :: Quiet_NAN ()) = نجاح كاذب V = NAN، ((ILOGB (القيمة) == ((ILOGB (INT) 0x80000000)) = النجاح الحقيقي U = 3.14، ((ILOGB (القيمة) == (( INT) 0x80000000)) = نجاح خاطئ W = INF، ((ILOGB (القيمة) == ((ILOGB (القيمة) 0x80000000)) = نجاح كاذب V = NAN، (isunordered (1.2345، القيمة)) = النجاح الحقيقي U = 3.14 (asunordered (1.2345، القيمة)) = نجاح كاذب W = INF، (isunordered (1.2345، القيمة)) = النجاح الخاطئ = NAN، (IS_IEEE754_NAN (القيمة)) = النجاح الحقيقي U = 3.14، (IS_IEEE754_NAN (القيمة)) = نجاح كاذب W = INF، (IS_IEEEE754_NAN (القيمة)) = نجاح كاذب [C:  My  My  Fience  So  282 ( كشف نان)]> g ++ foo.cpp -ffast-math && aالمترجم يدعي IEEE 754 = TRUE V = NAN، (STD :: ISNAN (القيمة)) = False فشل U = 3.14، (STD :: ISNAN (القيمة)) = النجاح الخاطئ W = INF، (STD :: ISNAN (القيمة) ) = النجاح الخاطئ v = nan، ((fpclassify (القيمة) == 0x0100)) = خطأ false U = 3.14، ((fpclassify (القيمة) == 0x0100)) = نجاح خاطئ W = INF == 0x0100)) = نجاح كاذبة V = NAN، ((القيمة! = القيمة) = FALSE فشل U = 3.14، ((القيمة! = القيمة)) = نجاح خاطئ W = INF، ((القيمة! = القيمة)) = النجاح الخاطئ V = NAN، ((القيمة == fp_info :: Quiet_nan ()) = النجاح الحقيقي U = 3.14، ((القيمة == FP_INFO :: Quiet_NAN ()) = فشل صحيح W = INF، ((القيمة == FP_INFO :: Quiet_nan ()) = فشل صحيح V = NAN، ((ILOGB (القيمة) == ((Int) 0x80000000)) = النجاح الحقيقي U = 3.14، ((ILOGB (القيمة) == (( INT) 0x80000000)) = نجاح كاذب W = INF، ((ILOGB (القيمة) == ((int) 0x80000000)) = نجاح كاذب V = NAN، (iSunordered (1.2345، القيمة)) = FALSE فشل U = 3.14 (is ... n، (is_ieee754_nan (القيمة)) = النجاح الحقيقي U = 3.14، (is_ieee754_nan (القيمة)) = نجاح خاطئ W = INF، (IS_IEEEE754_NAN (القيمة)) = نجاح خاطئ [C:  My  Forums  So  282 (كشف نان)]> _

النتائج مع Visual C ++:

C:  My  Forums  So  282 (كشف نان)]> CL / NOLOGO- 2> و 1 | البحث عن "++"Microsoft (R) C / C ++ تحسين برنامج التحويل البرمجي 19.00.23725 ل X86 [C:  My  My Forums  So  282 (كشف NAN)]> CL FOO.CPP / FEB && BFoo.CPP Compiler يدعي IEEE 754 = TRUE V = NAN، (STD :: ISNAN (القيمة)) = النجاح الحقيقي U = 3.14، (STD :: ISNAN (القيمة)) = النجاح الخاطئ W = INF، (STD :: Isnan (القيمة)) = النجاح الخاطئ v = nan، ((fpclassify (القيمة) == 2) = النجاح الحقيقي u = 3.14، ((fpclassify (القيمة) == 2) = نجاح خاطئ W = INF، ((fpclassify (القيمة) == 2) = نجاح خاطئ V = NAN، ((القيمة! = القيمة)) = النجاح الحقيقي U = 3.14، ((القيمة! = القيمة)) = نجاح خاطئ W = INF، ((القيمة! = القيمة)) = النجاح الخاطئ V = NAN، ((القيمة == FP_INFO :: CONEY_NAN ()) = خطأ FALSE U = 3.14، ((القيمة == fp_info :: Quiet_nan ())) = Halse Success W = INF ((القيمة == fp_info :: Quiet_nan ()) = نجاح كاذب V = NAN، ((ILOGB (القيمة) == 0x7FFFFFFF)) = True Success U = 3.14، ((ILOGB (القيمة) == 0x7FFFFFFF) = نجاحا كاذبا W = INF، ((ILOGB (القيمة) == 0x7FFFFFFF)) = فشل صحيح 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_IEEEE754_NAN (القيمة)) = نجاح كاذب [C:  My  Forums  So  282 (كشف NAN)]> CL FOO.CPP / FEB / FP: Fast && BFoo.CPP Compiler يدعي IEEE 754 = TRUE V = NAN، (STD :: ISNAN (القيمة)) = النجاح الحقيقي U = 3.14، (STD :: ISNAN (القيمة)) = النجاح الخاطئ W = INF، (STD :: Isnan (القيمة)) = النجاح الخاطئ v = nan، ((fpclassify (القيمة) == 2) = النجاح الحقيقي u = 3.14، ((fpclassify (القيمة) == 2) = نجاح خاطئ W = INF، ((fpclassify (القيمة) == 2) = نجاح خاطئ V = NAN، ((القيمة! = القيمة)) = النجاح الحقيقي U = 3.14، ((القيمة! = القيمة)) = نجاح خاطئ W = INF، ((القيمة! = القيمة)) = النجاح الخاطئ V = NAN، ((القيمة == FP_INFO :: CONEY_NAN ()) = خطأ FALSE U = 3.14، ((القيمة == fp_info :: Quiet_nan ())) = Halse Success W = INF ((القيمة == fp_info :: Quiet_nan ()) = نجاح كاذب V = NAN، ((ILOGB (القيمة) == 0x7FFFFFFF)) = True Success U = 3.14، ((ILOGB (القيمة) == 0x7FFFFFFF) = نجاحا كاذبا W = INF، ((ILOGB (القيمة) == 0x7FFFFFFF)) = فشل صحيح 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_IEEEE754_NAN (القيمة)) = نجاح خاطئ [C:  My  Forums  So  282 (كشف NAN)]> _

تلخيص النتائج المذكورة أعلاه، فقط الاختبار المباشر لتمثيل مستوى البت، باستخدام is_ieee754_nan تعمل الوظيفة المعرفة في برنامج الاختبار هذا، تعمل بشكل موثوق في جميع الحالات مع كل من G ++ و Visual C ++.


إضافة:
بعد نشر ما سبق، أصبحت على دراية بإمكانية اختبار NAN، المذكورة إجابة أخرى هنا، أي ((value < 0) == (value >= 0)). وبعد تحولت إلى العمل بشكل جيد مع Visual C ++ ولكن فشلت مع G ++ -ffast-math اختيار. لا يعمل اختبار Bitpattern المباشر فقط بشكل موثوق.

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 المحدد لنان يستخدم ما يلي:

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

بالنسبة لي، يمكن أن يكون الحل ماكرو لجعله مضمنا صراحة وبالتالي بسرعة كافية. كما يعمل لأي نوع تعويم. إنها قواعد على حقيقة أن الحالة الوحيدة عندما لا تساوي قيمة نفسها عندما تكون القيمة ليست رقما.

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

بالنظر إلى أن (x! = x) غير مضمون دائما لنان (مثل استخدام خيار الرياضيات -ffast)، فقد كنت أستخدم:

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

لا يمكن أن تكون الأرقام <0 و> 0، لذلك حقا يمر هذا فقط إذا كان الرقم ليس أقل من، ولا أكبر من أو يساوي الصفر. وهو أساسا لا يوجد عدد على الإطلاق، أو نان.

يمكنك أيضا استخدام هذا إذا كنت تفضل:

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

لست متأكدا من كيفية تأثر ذلك بالرياضيات، لذلك قد يختلف الأميال الخاص بك.

هذا يعمل:

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

الإخراج: Isnan.

يبدو لي أن أفضل نهج عبر النظام الأساسي الأفضل هو استخدام اتحاد واختبار نمط بت مزدوج للتحقق من NANS.

لم أختبر هذا الحل بدقة، وقد يكون هناك طريقة أكثر فعالية للعمل مع أنماط البت، لكنني أعتقد أنه يجب أن يعمل.

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

يقول معيار IEEE عندما يكون العرض الأساسي 1S و Mantissa ليست صفرية، الرقم NaNوبعد مزدوج هو 1 تسجيل قليلا، 11 بت غيره و 52 البتات مانتيسا. تحقق قليلا.

في X86-64، يمكنك الحصول على طرق سريعة للغاية للتحقق من نان واللوفينيتي، والتي تعمل بغض النظر عن -ffast-math خيار مترجم. فيf != f, std::isnan, std::isinf المحصول دائما false مع -ffast-math).


يمكن بسهولة القيام باختبار NAN، يمكن بسهولة إجراء الأرقام اللانهاية والمهدية عن طريق التحقق من الحد الأقصى للأسف. إن اللانهاية أقصى عرض مع Zero Mantissa، 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 تحدد التعليمات إشارة التكافؤ إذا كانت هناك حجة نان. هذه هي الطريقة std::isnan يعمل عندما لا -ffast-math يتم تحديد الخيارات.

هذا يكتشف اللانهاية وأيضا نان في Visual Studio عن طريق التحقق من ذلك هو حدود مزدوجة:

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

كتعليقات أعلاه الدولة أ! = لن تعمل في G ++ وبعض المحامرة الأخرى، ولكن هذه الخدعة يجب. قد لا تكون فعالة، لكنها لا تزال طريقة:

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

في الأساس، في G ++ (أنا لست متأكدا من الآخرين رغم ذلك) طباعة Printf "Nan" على٪ D أو٪ تنسيقات .f إذا كان المتغير ليس عددا صحيحا / تعويم صالحا. لذلك يقوم هذا الرمز بالتحقق من الطابع الأول من السلسلة ليكون "n" (كما هو الحال في "NAN")

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top