Steve Lorimer Steve Lorimer - 3 months ago 31
C++ Question

<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?


Answer

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