Search code examples
c++c++11c++14nancmath

<cmath> hides isnan in <math.h> in C++14 / C++11?


I have here a small test app which uses isnan from <math.h>:

#include <iostream>
#include <math.h>

int main()
{
    double d = NAN;

    std::cout << isnan(d) << '\n';

    return 0;
}

Build and run under 3 different standards:

$ g++ -std=c++98 main.cpp; ./a.out
1

$ g++ -std=c++11 main.cpp; ./a.out
1

$ g++ -std=c++14 main.cpp; ./a.out
1

Now we also include <cmath>, and test with both isnan and std::isnan:

#include <iostream>
#include <cmath>
#include <math.h>

int main()
{
    double d = NAN;

    std::cout << std::isnan(d) << '\n';
    std::cout << isnan(d) << '\n';

    return 0;
}

Build and run:

C++98 works

$ g++ -std=c++98 main.cpp; ./a.out
1
1

C++11 and C++14 don't, isnan is not found.

$ g++ -std=c++11 main.cpp
main.cpp: In function ‘int main()’:
main.cpp:10:25: error: ‘isnan’ was not declared in this scope
     std::cout << isnan(d) << '\n';
                         ^
main.cpp:10:25: note: suggested alternative:
In file included from main.cpp:3:0:
/usr/include/c++/5/cmath:641:5: note:   ‘std::isnan’
     isnan(_Tp __x)
     ^

$ g++ -std=c++14 main.cpp
main.cpp: In function ‘int main()’:
main.cpp:10:25: error: ‘isnan’ was not declared in this scope
     std::cout << isnan(d) << '\n';
                         ^
main.cpp:10:25: note: suggested alternative:
In file included from main.cpp:3:0:
/usr/include/c++/5/cmath:641:5: note:   ‘std::isnan’
     isnan(_Tp __x)
     ^

Note the order of inclusion is not important. If I include <cmath> before <math.h> or after, the result is the same.

Questions

  • Why is isnan gone?
  • Without having to go back and change old code to compile under the new standard, is there any way to fix this?

Solution

  • Briefly summarizing the pertinent points, mostly from Jonathan Wakely's excellent blog post:

    • glibc <2.23's math.h declares the obsolete X/Open int isnan(double); that is incompatible with the C99/C++11 version (bool isnan(double);).
    • glibc 2.23's math.h fixes this by not declaring the isnan function in C++11 or later.
    • All of them still define an isnan macro. #include <cmath> nukes that macro as required by the C++ standard.
    • GCC 6's libstdc++ provides its own special math.h header that declares a bool isnan(double); in the global namespace (unless the libc math.h declares the obsolete signature) and also nukes the macros as required by the standard.
    • Before GCC 6, #include <math.h> simply included the header from your libc, so the macro isn't nuked.
    • #include <cmath> always nukes the macros.

    Net result, in C++11 mode:

    glibc <  2.23, GCC <  6: <math.h> uses the macro; <cmath> uses obsolete signature
    glibc >= 2.23, GCC <  6: <math.h> uses the macro; <cmath> results in error
    glibc <  2.23, GCC >= 6: <math.h> and <cmath> use obsolete signature
    glibc >= 2.23, GCC >= 6: <math.h> and <cmath> use standard signature