Checking if a double (or float) is NaN in C++

后端 未结 21 1867
北恋
北恋 2020-11-22 05:10

Is there an isnan() function?

PS.: I\'m in MinGW (if that makes a difference).

I had this solved by using isnan() from , which doe

相关标签:
21条回答
  • 2020-11-22 05:34

    nan prevention

    My answer to this question is don't use retroactive checks for nan. Use preventive checks for divisions of the form 0.0/0.0 instead.

    #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 results from the operation 0.f/0.f, or 0.0/0.0. nan is a terrible nemesis to the stability of your code that must be detected and prevented very carefully1. The properties of nan that are different from normal numbers:

    • nan is toxic, (5*nan=nan)
    • nan is not equal to anything, not even itself (nan != nan)
    • nan not greater than anything (nan !> 0)
    • nan is not less than anything (nan !< 0)

    The last 2 properties listed are counter-logical and will result in odd behavior of code that relies on comparisons with a nan number (the 3rd last property is odd too but you're probably not ever going to see x != x ? in your code (unless you are checking for nan (unreliably))).

    In my own code, I noticed that nan values tend to produce difficult to find bugs. (Note how this is not the case for inf or -inf. (-inf < 0) returns TRUE, ( 0 < inf ) returns TRUE, and even (-inf < inf) returns TRUE. So, in my experience, the behavior of the code is often still as desired).

    what to do under nan

    What you want to happen under 0.0/0.0 must be handled as a special case, but what you do must depend on the numbers you expect to come out of the code.

    In the example above, the result of (0.f/FLT_MIN) will be 0, basically. You may want 0.0/0.0 to generate HUGE instead. So,

    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.
    

    So in the above, if x were 0.f, inf would result (which has pretty good/nondestructive behavior as mentioned above actually).

    Remember, integer division by 0 causes a runtime exception. So you must always check for integer division by 0. Just because 0.0/0.0 quietly evaluates to nan doesn't mean you can be lazy and not check for 0.0/0.0 before it happens.

    1 Checks for nan via x != x are sometimes unreliable (x != x being stripped out by some optimizing compilers that break IEEE compliance, specifically when the -ffast-math switch is enabled).

    0 讨论(0)
  • 2020-11-22 05:35

    Considering that (x != x) is not always guaranteed for NaN (such as if using the -ffast-math option), I've been using:

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

    Numbers can't be both < 0 and >= 0, so really this check only passes if the number is neither less than, nor greater than or equal to zero. Which is basically no number at all, or NaN.

    You could also use this if you prefer:

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

    I'm not sure how this is affected by -ffast-math though, so your mileage may vary.

    0 讨论(0)
  • 2020-11-22 05:39

    As for me the solution could be a macro to make it explicitly inline and thus fast enough. It also works for any float type. It bases on the fact that the only case when a value is not equals itself is when the value is not a number.

    #ifndef isnan
      #define isnan(a) (a != a)
    #endif
    
    0 讨论(0)
  • 2020-11-22 05:40

    You can use the isnan() function, but you need to include the C math library.

    #include <cmath>
    

    As this function is part of C99, it is not available everywhere. If your vendor does not supply the function, you can also define your own variant for compatibility.

    inline bool isnan(double x) {
        return x != x;
    }
    
    0 讨论(0)
  • 2020-11-22 05:42

    This works:

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

    output: isnan

    0 讨论(0)
  • 2020-11-22 05:43

    There is no isnan() function available in current C++ Standard Library. It was introduced in C99 and defined as a macro not a function. Elements of standard library defined by C99 are not part of current C++ standard ISO/IEC 14882:1998 neither its update ISO/IEC 14882:2003.

    In 2005 Technical Report 1 was proposed. The TR1 brings compatibility with C99 to C++. In spite of the fact it has never been officially adopted to become C++ standard, many (GCC 4.0+ or Visual C++ 9.0+ C++ implementations do provide TR1 features, all of them or only some (Visual C++ 9.0 does not provide C99 math functions).

    If TR1 is available, then cmath includes C99 elements like isnan(), isfinite(), etc. but they are defined as functions, not macros, usually in std::tr1:: namespace, though many implementations (i.e. GCC 4+ on Linux or in XCode on Mac OS X 10.5+) inject them directly to std::, so std::isnan is well defined.

    Moreover, some implementations of C++ still make C99 isnan() macro available for C++ (included through cmath or math.h), what may cause more confusions and developers may assume it's a standard behaviour.

    A note about Viusal C++, as mentioned above, it does not provide std::isnan neither std::tr1::isnan, but it provides an extension function defined as _isnan() which has been available since Visual C++ 6.0

    On XCode, there is even more fun. As mentioned, GCC 4+ defines std::isnan. For older versions of compiler and library form XCode, it seems (here is relevant discussion), haven't had chance to check myself) two functions are defined, __inline_isnand() on Intel and __isnand() on Power PC.

    0 讨论(0)
提交回复
热议问题