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

21

422

Is there an isnan() function?

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

I had this solved by using isnan() from <math.h>, which doesn't exist in <cmath>, which I was #includeing at first.

Glamour answered 20/2, 2009 at 18:9 Comment(5)
I not pure you can do it portably. Who says C++ requires IEEE754?Sophronia
See also: how do I make a portable isnan/isinf functionXiphoid
Just a note, 1 oz of prevention is better than 1 lb of cure. In other words, preventing 0.f/0.f from ever being executed is far better than retroactively checking for nan's in your code. nan is can be terribly destructive to your program, if allowed to proliferate it can introduce hard to find bugs. This is because nan is toxic, (5*nan=nan), nan is not equal to anything (nan != nan), nan not greater than anything (nan !> 0), nan is not less than anything (nan !< 0).Xiphoid
@bobobobo: That's a feature, allowing centralized error checking. Just like exceptions vs return values.Blomquist
Why doesn't <cmath> have isnan()? It's in std::Preuss
M
404

According to the IEEE standard, NaN values have the odd property that comparisons involving them are always false. That is, for a float f, f != f will be true only if f is NaN.

Note that, as some comments below have pointed out, not all compilers respect this when optimizing code.

For any compiler which claims to use IEEE floating point, this trick should work. But I can't guarantee that it will work in practice. Check with your compiler, if in doubt.

Morganne answered 20/2, 2009 at 18:17 Comment(14)
Isn't it possible that the compiler will optimize this out though? (I don't know because I haven't used C++ for a long time)Secession
The compiler had better not remove this if running in an IEEE mode. Check the documentation for your compiler, of course...Bathelda
The compiler will not optimize this out. The only caveat is that this property applies to IEEE floating point values, but may not apply to other formats. Common platfors use IEEE so this is guaranteed to work on those, but on platforms using different fp formats, this trick doesn't work.Morganne
-1 only works in theory, not in practice: compilers such as g++ (with -fastmath) screw that up. the only general way, until c++0x, is to test for bitpattern.Augury
@Alf: The documentation for the -ffast-math option explicitly says that it can result in incorrect output for programs which depend on an exact implementation if IEEE or ISO rules/specifications for math functions. Without that option enabled, using x != x is a perfectly valid and portable way of testing for NaN.Triste
@Adam: the documentation does openly state that it's non-conforming, yes. and yes i have encountered that argument before, discussing this at length with Gabriel Dos Reis. it's commonly used to defend the design, in a circular argument (i don't know if you intended to associate to that, but worth knowing about -- it's flame war stuff). your conclusion that x != x is valid without that option does not follow logically. it might be true for a particular version of g++, or not. anyway, you generally have no way to guarantee that fastmath option will not be used.Augury
@Alf: No I was not aware of your discussion with Gabriel Dos Reis. Steve Jessop made a great point in the other question about assuming IEEE representation. If you assume IEEE 754 and that the compiler is operating in a conforming manner (i.e. without the -ffast-math option), then x != x is a valid and portable solution. You can even test for -ffast-math by testing for the __FAST_MATH__ macro and switch to a different implementation in that case (e.g. use unions and bit twiddling).Triste
@Adaam: code that uses this technique fails when compiled with options where x != x does not hold for NaN, not just for g++. And yes you can test for each compiler (which is an herculean task), and switch to bit-level testing when x != x doesn't work. That means including both x != x and a nore generally valid test in your code (to be used when former fails) -- which is nonsensical.Augury
You might want to declare it volatile. That way it won't get optimized.From
The isnan utility included in math.h with xCode, includes the following note: -ffast-math, among other things, implies that NaNs don't happen, which allows the compiler to optimize away checks like x != x, which might lead to things like isnan(NaN) returning false. Thus, if you compile with -ffast-math, _actual function calls are generated for these utilities._Xiphoid
FWIW, As per my testing on g++ 7.3.0 with -std=c++11 on and default optimization level, both nan==nan and nan!=nan evaluate to falseKings
f != f doesn't work with gcc/clang option -ffinite-math-onlyWasson
std::isnanJanettajanette
[...] NaN values have the odd property that comparisons involving them are always false. That is, for a float f, f != f will be true only if f is NaN. This is a direct contradiction. != is a comparison. I think you mean to say that according to IEEE that == between NaN's is false, and != is true. Is that right? It certainly seems to be the implementation for gcc and clang.Deandra
B
241

First solution: if you are using C++11

Since this was asked there were a bit of new developments: it is important to know that std::isnan() is part of C++11

Synopsis

Defined in header <cmath>

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

Determines if the given floating point number arg is not-a-number (NaN).

Parameters

arg: floating point value

Return value

true if arg is NaN, false otherwise

Reference

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

Please note that this is incompatible with -fast-math if you use g++, see below for other suggestions.


Other solutions: if you using non C++11 compliant tools

For C99, in C, this is implemented as a macro isnan(c)that returns an int value. The type of x shall be float, double or long double.

Various vendors may or may not include or not a function isnan().

The supposedly portable way to check for NaN is to use the IEEE 754 property that NaN is not equal to itself: i.e. x == x will be false for x being NaN.

However the last option may not work with every compiler and some settings (particularly optimisation settings), so in last resort, you can always check the bit pattern ...

Bascomb answered 30/5, 2013 at 13:30 Comment(9)
Definitely deserves to be the accepted answer and deserves more upvotes. Thanks for the tipDarrickdarrill
−1 std::isnan is still an ungood recommendation as of Feb 2017, since it doesn't work with g++'s floating point optimization.Augury
@Cheersandhth.-Alf: is this option IEEE compliant ? The answer has been editedBascomb
@BlueTrin: Both x != x and isnan are required to work for IEEE 754 compliance. Regarding the latter, the IEEE 754-2008 standard states that “Implementations shall provide the following non-computational operations for all supported arithmetic formats” and “isNaN(x) is true if and only if x is a NaN”. For checking conformance that standard requires is754version1985() and is754version2008(), where C++ instead offers std::numeric_limits<Fp>::is_iec559() (IEC 559 is the same standard). Unfortunately with -ffast-math optimization, e.g. g++ claims conformance but is non-conforming.Augury
Re the final “you can always check the bit pattern ...”, which as of Feb 2017 is the only reliable way, code for that is given in my #2 answer.Augury
@Cheersandhth.-Alf Which specific gcc flags are you referencing? In general, if your code relies on IEEE 754 compliance, you should be very careful about which optimization flags you use.Paternalism
@Thucydides411: "should be very careful", maybe more than you think. There's a pretty extensive test. including compiler invokations, in my answer below this, (https://mcmap.net/q/86002/-checking-if-a-double-or-float-is-nan-in-c). Summary: only checking of the bits of the number's representation, works reliably.Augury
Warning: isnan(x) does not work with option -ffinite-math-only in gcc and clangWasson
isnan works as intended. Some compilers are simply willing to give you the option to break everything if you really want to. Don't blame std::isnan for that.Extravagancy
K
232

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.

Kaleidoscope answered 23/1, 2010 at 16:19 Comment(3)
Everybody wants these functions like isNan or isInfinity. Why do the people in charge not simply include in their standards???? - I will try to find out how to get in charge and put my vote in for this. Seriously.Undefined
@Undefined In charge yet?Fungus
This answer should be updated since std::isnan is now part of the C++11 standard and support has spread out. std::isnan was implemented in Visual Studio starting with Visual Studio 2013. Maybe @Undefined got in charge :-)Seleucid
I
83

There is also a header-only library present in Boost that have neat tools to deal with floating point datatypes

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

You get the following functions:

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

If you have time then have a look at whole Math toolkit from Boost, it has many useful tools and is growing quickly.

Also when dealing with floating and non-floating points it might be a good idea to look at the Numeric Conversions.

Impearl answered 20/2, 2009 at 20:40 Comment(2)
it was added in Boost 1.35 (I've just found my program doesn't compile on old linux distro).Hamamelidaceous
if you compile with the option --fast-math then this function will not work as expected.Routh
A
44

There are three "official" ways: posix isnan macro, c++0x isnan function template, or visual c++ _isnan function.

Unfortunately it's rather impractical to detect which of those to use.

And unfortunately, there's no reliable way to detect whether you have IEEE 754 representation with NaNs. The standard library offers an official such way (numeric_limits<double>::is_iec559). But in practice compilers such as g++ screw that up.

In theory one could use simply x != x, but compilers such as g++ and visual c++ screw that up.

So in the end, test for the specific NaN bitpatterns, assuming (and hopefully enforcing, at some point!) a particular representation such as IEEE 754.


EDIT: as an example of "compilers such as g++ … screw that up", consider

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

Compiling with g++ (TDM-2 mingw32) 4.4.1:

C:\test> type "C:\Program Files\@commands\gnuc.bat"
@rem -finput-charset=windows-1252
@g++ -O -pedantic -std=c++98 -Wall -Wwrite-strings %* -Wno-long-long

C:\test> gnuc x.cpp

C:\test> a && echo works... || echo !failed
works...

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

C:\test> a && echo works... || echo !failed
Assertion failed: a != b, file x.cpp, line 6

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
!failed

C:\test> _
Augury answered 26/3, 2011 at 9:8 Comment(5)
@Alf: Your example works as expected for me on both Mac OS X and Linux on various versions of g++ between 4.0 and 4.5. The documentation for the -ffast-math option explicitly says that it can result in incorrect output for programs which depend on an exact implementation if IEEE or ISO rules/specifications for math functions. Without that option enabled, using x != x is a perfectly valid and portable way of testing for NaN.Triste
@Adam: What you're missing is that the C++ standard doesn't require IEEE representation or math for floats. As far as the man page tells you, gcc -ffast-math is still a conforming C++ implementation (well, assuming it gets numeric_limits::is_iec559 right, it is, although Alf suggests above that it doesn't): C++ code relying on IEEE is not portable C++ and has no right to expect implementations to provide it.Home
And Alf's right, quick test on gcc 4.3.4 and is_iec559 is true with -ffast-math. So the problem here is that GCC's docs for -ffast-math only say that it's non-IEEE/ISO for math functions, whereas they should say that it's non-C++, because its implementation of numeric_limits is borked. I would guess that GCC can't always tell at the time that template is defined, whether the eventual backend actually has conforming floats, and so doesn't even try. IIRC there are similar issues in the outstanding bug list for GCC's C99 conformance.Home
@Alf, @Steve, I didn't know C++ standard has no specification about floating-point values. It's pretty shocking to me. It looks better handling IEEE 754 and NaN as a platform specific extension instead of standard. Isn't it? And can I expect the any kind of isnan() or IEEE754 added in C++0x?Addax
@Eonil: C++0x still has for example "The value representation of floating-point types is implementation-defined". C and C++ both aim to support implementations on machines with no floating-point hardware, and proper IEEE 754 floats can be quite a bit slower to emulate than reasonably-accurate alternatives. The theory is you can assert is_iec559 if you need IEEE, in practice that doesn't appear to work on GCC. C++0x does have an isnan function, but since GCC doesn't correctly implement is_iec559 now, I guess it won't in C++0x either, and -ffast-math might well break its isnan.Home
H
39

There is an std::isnan if you compiler supports c99 extensions, but I'm not sure if mingw does.

Here is a small function which should work if your compiler doesn't have the standard function:

bool custom_isnan(double var)
{
    volatile double d = var;
    return d != d;
}
Horsepower answered 20/2, 2009 at 18:14 Comment(4)
When doing that their is a chance the compiler will optimize the comparison out, always returning true.Horsepower
No there isn't. A compiler that does that is broken. You might as well say that there's a chance that the standard library isnan returns the wrong result. Technically true, the compiler could be buggy, but in practice, Not Gonna Happen. Same as var != var. It works because that's how IEEE floating point values are defined.Morganne
if -ffast-math is set, isnan() will fail to return the correct result for gcc. Of course, this optimization is documented as breaking IEEE semantics...Forbid
If -ffast-math is set, then the compiler is buggy. Or rather, if -ffast-math is set, all bets are off and you can't rely on NaNs anyway.Curvaceous
B
25

You can use numeric_limits<float>::quiet_NaN( ) defined in the limits standard library to test with. There's a separate constant defined for 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;
}

I don't know if this works on all platforms, as I only tested with g++ on Linux.

Boyes answered 20/2, 2009 at 19:18 Comment(2)
Watch out, though--there appears to be a bug in numeric_limits in GCC version 3.2.3, since it returns 0.0 for quiet_NaN. Later versions of GCC are okay in my experience.Krahling
@Nathan: Good to know. I'm using version 4.3.2, so I'm well out of the woods.Boyes
C
21

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;
}
Chino answered 20/2, 2009 at 18:18 Comment(2)
I was using <cmath> and there's no isnan in it! incidentally I found out that there is an isnan in <math.h>Glamour
As I said, this is part of C99. As C99 is not part of any current C++ standard, I provided the alternative. But as it is likely that isnan() will be included in an upcoming C++ standard, I put a #ifndef directive around it.Chino
A
17

As of C++14 there are a number of ways to test if a floating point number value is a NaN.

Of these ways, only checking of the bits of the number's representation, works reliably, as noted in my original answer. In particular, std::isnan and the often proposed check v != v, do not work reliably and should not be used, lest your code stops working correctly when someone decides that floating point optimization is needed, and asks the compiler to do that. This situation can change, compilers can get more conforming, but for this issue that hasn't happened in the 6 years since the original answer.

For about 6 years my original answer was the selected solution for this question, which was OK. But recently a highly upvoted answer recommending the unreliable v != v test has been selected. Hence this additional more up-to-date answer (we now have the C++11 and C++14 standards, and C++17 on the horizon).


The main ways to check for NaN-ness, as of C++14, are:

  • std::isnan(value) )
    is the intended standard library way since C++11. isnan apparently conflicts with the Posix macro of the same name, but in practice that isn't a problem. The main problem is that when floating point arithmetic optimization is requested, then with at least one main compiler, namely g++, std::isnan returns false for NaN argument.

  • (fpclassify(value) == FP_NAN) )
    Suffers from the same problem as std::isnan, i.e., is not reliable.

  • (value != value) )
    Recommended in many SO answers. Suffers from the same problem as std::isnan, i.e., is not reliable.

  • (value == Fp_info::quiet_NaN()) )
    This is a test that with standard behavior should not detect NaNs, but that with the optimized behavior maybe could detect NaNs (due to optimized code just comparing the bitlevel representations directly), and perhaps combined with another way to cover the standard un-optimized behavior, could reliably detect NaN. Unfortunately it turned out to not work reliably.

  • (ilogb(value) == FP_ILOGBNAN) )
    Suffers from the same problem as std::isnan, i.e., is not reliable.

  • isunordered(1.2345, value) )
    Suffers from the same problem as std::isnan, i.e., is not reliable.

  • is_ieee754_nan( value ) )
    This isn't a standard function. It's checking of the bits according to the IEEE 754 standard. It's completely reliable but the code is somewhat system-dependent.


In the following complete test code “success” is whether an expression reports Nan-ness of the value. For most expressions this measure of success, the goal of detecting NaNs and only NaNs, corresponds to their standard semantics. For the (value == Fp_info::quiet_NaN()) ) expression, however, the standard behavior is that it doesn't work as a NaN-detector.

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

Results with g++ (note again that the standard behavior of (value == Fp_info::quiet_NaN()) is that it doesn't work as a NaN-detector, it's just very much of practical interest here):

[C:\my\forums\so\282  (detect NaN)]
> g++ --version | find "++"
g++ (x86_64-win32-sjlj-rev1, Built by MinGW-W64 project) 6.3.0

[C:\my\forums\so\282  (detect NaN)]
> g++ foo.cpp && a
Compiler claims IEEE 754 = true

v = nan, (std::isnan(value)) = true                           Success
u = 3.14, (std::isnan(value)) = false                         Success
w = inf, (std::isnan(value)) = false                          Success

v = nan, ((fpclassify(value) == 0x0100)) = true               Success
u = 3.14, ((fpclassify(value) == 0x0100)) = false             Success
w = inf, ((fpclassify(value) == 0x0100)) = false              Success

v = nan, ((value != value)) = true                            Success
u = 3.14, ((value != value)) = false                          Success
w = inf, ((value != value)) = false                           Success

v = nan, ((value == Fp_info::quiet_NaN())) = false            FAILED
u = 3.14, ((value == Fp_info::quiet_NaN())) = false           Success
w = inf, ((value == Fp_info::quiet_NaN())) = false            Success

v = nan, ((ilogb(value) == ((int)0x80000000))) = true         Success
u = 3.14, ((ilogb(value) == ((int)0x80000000))) = false       Success
w = inf, ((ilogb(value) == ((int)0x80000000))) = false        Success

v = nan, (isunordered(1.2345, value)) = true                  Success
u = 3.14, (isunordered(1.2345, value)) = false                Success
w = inf, (isunordered(1.2345, value)) = false                 Success

v = nan, (is_ieee754_nan( value )) = true                     Success
u = 3.14, (is_ieee754_nan( value )) = false                   Success
w = inf, (is_ieee754_nan( value )) = false                    Success

[C:\my\forums\so\282  (detect NaN)]
> g++ foo.cpp -ffast-math && a
Compiler claims IEEE 754 = true

v = nan, (std::isnan(value)) = false                          FAILED
u = 3.14, (std::isnan(value)) = false                         Success
w = inf, (std::isnan(value)) = false                          Success

v = nan, ((fpclassify(value) == 0x0100)) = false              FAILED
u = 3.14, ((fpclassify(value) == 0x0100)) = false             Success
w = inf, ((fpclassify(value) == 0x0100)) = false              Success

v = nan, ((value != value)) = false                           FAILED
u = 3.14, ((value != value)) = false                          Success
w = inf, ((value != value)) = false                           Success

v = nan, ((value == Fp_info::quiet_NaN())) = true             Success
u = 3.14, ((value == Fp_info::quiet_NaN())) = true            FAILED
w = inf, ((value == Fp_info::quiet_NaN())) = true             FAILED

v = nan, ((ilogb(value) == ((int)0x80000000))) = true         Success
u = 3.14, ((ilogb(value) == ((int)0x80000000))) = false       Success
w = inf, ((ilogb(value) == ((int)0x80000000))) = false        Success

v = nan, (isunordered(1.2345, value)) = false                 FAILED
u = 3.14, (isunordered(1.2345, value)) = false                Success
w = inf, (isunordered(1.2345, value)) = false                 Success

v = nan, (is_ieee754_nan( value )) = true                     Success
u = 3.14, (is_ieee754_nan( value )) = false                   Success
w = inf, (is_ieee754_nan( value )) = false                    Success

[C:\my\forums\so\282  (detect NaN)]
> _

Results with Visual C++:

[C:\my\forums\so\282  (detect NaN)]
> cl /nologo- 2>&1 | find "++"
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23725 for x86

[C:\my\forums\so\282  (detect NaN)]
> cl foo.cpp /Feb && b
foo.cpp
Compiler claims IEEE 754 = true

v = nan, (std::isnan(value)) = true                           Success
u = 3.14, (std::isnan(value)) = false                         Success
w = inf, (std::isnan(value)) = false                          Success

v = nan, ((fpclassify(value) == 2)) = true                    Success
u = 3.14, ((fpclassify(value) == 2)) = false                  Success
w = inf, ((fpclassify(value) == 2)) = false                   Success

v = nan, ((value != value)) = true                            Success
u = 3.14, ((value != value)) = false                          Success
w = inf, ((value != value)) = false                           Success

v = nan, ((value == Fp_info::quiet_NaN())) = false            FAILED
u = 3.14, ((value == Fp_info::quiet_NaN())) = false           Success
w = inf, ((value == Fp_info::quiet_NaN())) = false            Success

v = nan, ((ilogb(value) == 0x7fffffff)) = true                Success
u = 3.14, ((ilogb(value) == 0x7fffffff)) = false              Success
w = inf, ((ilogb(value) == 0x7fffffff)) = true                FAILED

v = nan, (isunordered(1.2345, value)) = true                  Success
u = 3.14, (isunordered(1.2345, value)) = false                Success
w = inf, (isunordered(1.2345, value)) = false                 Success

v = nan, (is_ieee754_nan( value )) = true                     Success
u = 3.14, (is_ieee754_nan( value )) = false                   Success
w = inf, (is_ieee754_nan( value )) = false                    Success

[C:\my\forums\so\282  (detect NaN)]
> cl foo.cpp /Feb /fp:fast && b
foo.cpp
Compiler claims IEEE 754 = true

v = nan, (std::isnan(value)) = true                           Success
u = 3.14, (std::isnan(value)) = false                         Success
w = inf, (std::isnan(value)) = false                          Success

v = nan, ((fpclassify(value) == 2)) = true                    Success
u = 3.14, ((fpclassify(value) == 2)) = false                  Success
w = inf, ((fpclassify(value) == 2)) = false                   Success

v = nan, ((value != value)) = true                            Success
u = 3.14, ((value != value)) = false                          Success
w = inf, ((value != value)) = false                           Success

v = nan, ((value == Fp_info::quiet_NaN())) = false            FAILED
u = 3.14, ((value == Fp_info::quiet_NaN())) = false           Success
w = inf, ((value == Fp_info::quiet_NaN())) = false            Success

v = nan, ((ilogb(value) == 0x7fffffff)) = true                Success
u = 3.14, ((ilogb(value) == 0x7fffffff)) = false              Success
w = inf, ((ilogb(value) == 0x7fffffff)) = true                FAILED

v = nan, (isunordered(1.2345, value)) = true                  Success
u = 3.14, (isunordered(1.2345, value)) = false                Success
w = inf, (isunordered(1.2345, value)) = false                 Success

v = nan, (is_ieee754_nan( value )) = true                     Success
u = 3.14, (is_ieee754_nan( value )) = false                   Success
w = inf, (is_ieee754_nan( value )) = false                    Success

[C:\my\forums\so\282  (detect NaN)]
> _

Summing up the above results, only direct testing of the bit-level representation, using the is_ieee754_nan function defined in this test program, worked reliably in all cases with both g++ and Visual C++.


Addendum:
After posting the above I became aware of yet another possible to test for NaN, mentioned in another answer here, namely ((value < 0) == (value >= 0)). That turned out to work fine with Visual C++ but failed with g++'s -ffast-math option. Only direct bitpattern testing works reliably.

Augury answered 9/2, 2017 at 13:49 Comment(2)
I looked into the fpclassify function further. The glibc implementation does check bit patterns and should be robust against fast-math. However, the include files (/usr/include/math.h and cmath) direct fpclassify to a compiler built-in function. At least in clang, that built-in uses a self-comparison, which fails under fast-math. The glibc version appears to be available as __fpclassify. In a simple test, __fpclassify(x) == FP_NAN did work under fast-math.Rutan
std::isnan is perfectly reliable. gcc is simply unreliable, if you are willing to enable an option that tells it to throw the rules out the window. The simple solution: don't do that.Extravagancy
C
12

The following code uses the definition of NAN (all exponent bits set, at least one fractional bit set) and assumes that sizeof(int) = sizeof(float) = 4. You can look up NAN in Wikipedia for the details.

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

Commute answered 5/4, 2011 at 1:53 Comment(2)
I believe this would also work on big endian platforms. The literal 0x7fffffff would simply sit in memory as ff ff ff 7f. value has the same ordering as does 0x7f800000, so all the operations line up (there's no swapping of bytes). I'd be interested if someone could test this on a big endian platform.Antechamber
0x7fff1234 is also a NaN. So is 0xffffffffJovitajovitah
X
12

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

Xiphoid answered 16/8, 2013 at 22:41 Comment(4)
Thanks for pointing this out; programming like that would definitely help with the problem as such. But next time, please try not to abuse the text formatting features too much. Switching font sizes, weight and style like that are making it really hard to read.Calyptra
Note that 0.0/0.0 is not the only operation that might result in a NaN. The square root of a negative number returns NaN. The cosine of +infinity returns NaN as well. the operation acos(x) where x is not in the range [0,pi] can also results in NaN. In a nutshell, one has to be extra careful to also look at these potentially risky operations, not only to 0.0/0.0.Anesthesia
Totally agree with Boris. In my experience, NaN practically always came from something like sqrt(-1.302e-53), i.e. close-to-zero intermediate computation results being fed into sqrt without checking for negativity.Wulfila
"Preventing NaNs" means that you need to get inside all basic arithmetic operations, not just division. You'll need to watch watch out for ∞/∞, 0 * ∞, ∞ % x, x % 0, ∞ - ∞, 0^0, ∞^0, among many others. Being "preventative" with such basic arithmetic operations means that you'll completely tank your performance (and likely miss additional cases you didn't think of).Jovitajovitah
E
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);
}

This works if sizeof(int) is 4 and sizeof(long long) is 8.

During run time it is only comparison, castings do not take any time. It just changes comparison flags configuration to check equality.

Emarginate answered 3/10, 2013 at 10:32 Comment(2)
Also note, it's limited to IEEE 754 representation.Augury
Note that this cast breaks the strict aliasing rule of g++, and that compiler has been known to do Unmentionable Things™ when it detects formal UB. Instead of efficient casts, with g++ you need to use memcpy, through a byte array to be sure. Code for that in my #2 answer.Augury
R
4

A possible solution that would not depend on the specific IEEE representation for NaN used would be the following:

template<class T>
bool isnan( T f ) {
    T _nan =  (T)0.0/(T)0.0;
    return 0 == memcmp( (void*)&f, (void*)&_nan, sizeof(T) );
}
Ratty answered 3/4, 2012 at 22:3 Comment(1)
Single-precision floating point has over 8 million legitimate and different bit represenations for NaN, so you'll need to add some more comparisons. :)Jovitajovitah
F
4

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.

Forecast answered 26/1, 2016 at 1:6 Comment(2)
This is actually flawed the same way f != f is flawed too. I have seen llvm optimising an almost identical piece of code away. The optimizer can propagate the information about the first comparison and figure out that the second comparsion may never be true if the first one is. (if the compiler strictly obeys IEEE rules f != f is much simpler anyway)Fader
Doesn't work with g++'s -ffast-math option. Works with Visual C++. See (https://mcmap.net/q/86002/-checking-if-a-double-or-float-is-nan-in-c).Augury
T
3

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
Tangency answered 11/12, 2012 at 9:8 Comment(2)
This is one of the best answers to this question! Thank you for sharing.Bowlin
Other answers indicate that this can fail with the -ffast-math option set.Wilkie
N
3

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

Nethermost answered 3/8, 2014 at 22:38 Comment(0)
O
1

It seems to me that the best truly cross-platform approach would be to use a union and to test the bit pattern of the double to check for NaNs.

I have not thoroughly tested this solution, and there may be a more efficient way of working with the bit patterns, but I think that it should work.

#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;
}
Osmometer answered 8/10, 2016 at 17:20 Comment(1)
Note that "it's undefined behavior to read from the member of the union that wasn't most recently written". So this use of a union to type-pun between two types may not work as desired (:sad_panda:). The correct (albeit not actually quite as portable as desired) way would be to avoid the union altogether, and do a memcpy from the double into a different uint64_t variable, then do the test using that helper variable.Cuomo
W
1

On x86-64 you can have extremely fast methods for checking for NaN and infinity, which work regardless of -ffast-math compiler option. (f != f, std::isnan, std::isinf always yield false with -ffast-math).


Testing for NaN, infinity and finite numbers can easily be done by checking for maximum exponent. infinity is maximum exponent with zero mantissa, NaN is maximum exponent and non-zero mantissa. The exponent is stored in the next bits after the topmost sign bit, so that we can just left shift to get rid of the sign bit and make the exponent the topmost bits, no masking (operator&) is necessary:

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

The std versions of isinf and isfinite load 2 double/float constants from .data segment and in the worst case scenario they can cause 2 data cache misses. The above versions do not load any data, inf_double_shl1 and inf_float_shl1 constants get encoded as immediate operands into the assembly instructions.


Faster isnan2 is just 2 assembly instructions:

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

Uses the fact that ucomisd instruction sets parity flag if any argument is NaN. This is how std::isnan works when no -ffast-math options is specified.

Wey answered 3/9, 2019 at 11:19 Comment(0)
C
-1

The IEEE standard says when the exponent is all 1s and the mantissa is not zero, the number is a NaN. Double is 1 sign bit, 11 exponent bits and 52 mantissa bits. Do a bit check.

Crept answered 31/1, 2014 at 19:26 Comment(0)
C
-3

As comments above state a != a will not work in g++ and some other compilers, but this trick should. It may not be as efficient, but it's still a way:

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

Basically, in g++ (I am not sure about others though) printf prints 'nan' on %d or %.f formats if variable is not a valid integer/float. Therefore this code is checking for the first character of string to be 'n' (as in "nan")

Choate answered 26/6, 2013 at 21:33 Comment(3)
Wouldn't that cause a buffer overflow if a = 234324.0f ?Inflight
Yes t'will, or 340282346638528859811704183484516925440.000 if a=FLT_MAX. He'd have to use char s[7]; sprintf(s, "%.0g", a);, which'll be 6 chrs if a=-FLT_MAX, or -3e+38Xiphoid
I used the same basic idea you are presenting. But it also does not work if the value is "-nan". But making the buffer larger and using if(strchr(s,"nan")!=nullptr) as the condition should do the trick.Lalalalage
R
-3

This detects infinity and also NaN in Visual Studio by checking it is within double limits:

//#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;
Recondition answered 30/11, 2018 at 8:45 Comment(1)
Check the definition of FLT_MIN, DBL_MIN and LDBL_MIN more carefully. These are defined to be the smallest normalized values for each type. For example, single-precision has over 8 million legitimate denorm values that are greater than zero and less than FLT_MIN (and are not NaN).Jovitajovitah

© 2022 - 2024 — McMap. All rights reserved.