24

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?
Community
  • 1
  • 1
Steve Lorimer
  • 27,059
  • 17
  • 118
  • 213
  • Is Clang using libc++, or the same libstdc++ that gcc is? – ildjarn Aug 24 '16 at 17:58
  • @ildjarn `ldd` says it's the same (`/usr/lib/x86_64-linux-gnu/libstdc++.so.6`). I've edited the question to remove the clang part. – Steve Lorimer Aug 24 '16 at 18:01
  • Weird problem.. What if you swap strings including cmath and math.h? As for me, I'm avoiding usage of c* headers like cmath – Anton Malyshev Aug 24 '16 at 18:02
  • @AntonMalyshev are you asking about order of inclusion? That makes no difference. – Steve Lorimer Aug 24 '16 at 18:03
  • 2
    What version of the compilers are you using? I cannot reproduce with [g++](http://coliru.stacked-crooked.com/a/79a28e0056232dd1) or [clang](http://coliru.stacked-crooked.com/a/393e772c6846ef3c) on coliru. – NathanOliver Aug 24 '16 at 18:04
  • @AntonMalyshev can't avoid the usage of the c* headers - this is old code which needs to be built as part of a larger codebase built using newer standards – Steve Lorimer Aug 24 '16 at 18:04
  • @NathanOliver I'm using `g++ 5.4.0`. glibc version is `2.23` – Steve Lorimer Aug 24 '16 at 18:06
  • hmm. Apparently coliru uses ldd (Ubuntu EGLIBC 2.15-0ubuntu10.13) 2.15 – NathanOliver Aug 24 '16 at 18:09
  • @NathanOliver following your comment I tried on [Wandbox](http://melpon.org/wandbox). It doesn't have `gcc-5.4.0` but it does have `5.3.0` and `6.1.0`, and I can't replicate on either... Very strange! – Steve Lorimer Aug 24 '16 at 18:10
  • Don't use both cmath and math.h... – Ven Aug 24 '16 at 18:10
  • 1
    It looks like it may be a bug with the library. [gcc.godbolt.org](http://gcc.godbolt.org/) fails 4.9.2 through 5.3 but works on 6.1 – NathanOliver Aug 24 '16 at 18:13
  • @Ven, I'm not. The code in question uses `math.h`. However, I assume some other include is including something which is including something which ultimately includes `cmath`, so both are in scope. Hence the compile failure, and hence me writing this small app to diagnose what is happening – Steve Lorimer Aug 24 '16 at 18:13
  • @NathanOliver that's very interesting! [gcc.godbolt.org](http://gcc.godbolt.org) fails to compile for 5.3, while [wandbox](http://melpon.org/wandbox) works for 5.3. – Steve Lorimer Aug 24 '16 at 18:16
  • Very bizarre. It will be interesting to see what the cause of this inconsistency is. Sorry I could only confuse the problem more :) – NathanOliver Aug 24 '16 at 18:21
  • @NathanOliver no, thanks for helping confirm I'm not crazy! I'll have a look on bugzilla and see if I can find anything – Steve Lorimer Aug 24 '16 at 18:26
  • @NathanOliver I've run this on Centos gcc-4.9.1 and gcc-5.2.1 and it works on both. Running it on Ubuntu gcc-4.9.3 works, but gcc-5.4.0 doesn't. Given godbolt fails for some, but wandbox doesn't... well, I'm not sure what to think?! – Steve Lorimer Aug 24 '16 at 18:28
  • 2
    @SteveLorimer If you do not find something out in a couple days give me a ping and I will place a bounty. I would really love to know what is going on. – NathanOliver Aug 24 '16 at 20:30

3 Answers3

23

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
T.C.
  • 133,968
  • 17
  • 288
  • 421
9

If you look inside <cmath> from GCC, it has this:

. . .
#include <math.h>
. . .
#undef isnan

That's why the order doesn't matter - whenever you #include <cmath>, <math.h> is auto-included and its contents is (partially) nuked.

Attempting to include it again will have no effect because of #ifndef _MATH_H.


Now, what does the standard have to say about this behavior?

[depr.c.headers]:

... every C header, each of which has a name of the form name.h, behaves as if each name placed in the standard library namespace by the corresponding cname header is placed within the global namespace scope. It is unspecified whether these names are first declared or defined within namespace scope ([basic.scope.namespace]) of the namespace std and are then injected into the global namespace scope by explicit using-declarations ([namespace.udecl]).

[ Example: The header <cstdlib> assuredly provides its declarations and definitions within the namespace std. It may also provide these names within the global namespace. The header <stdlib.h> assuredly provides the same declarations and definitions within the global namespace, much as in the C Standard. It may also provide these names within the namespace std. — end example ]

So it's OK that <cmath> does not provide isnan in the global namespace.

But it's a gray area what should happen when both are included in one compilation unit, although one could argue that the statement above implies that both versions must interoperate, in which case it would be a bug in GCC/libstdc++ (some versions).

rustyx
  • 80,671
  • 25
  • 200
  • 267
3

A lot of the functions inside math.h are actually macros. Since this is not idiomatic c++ the header cmath contains following code:

    ...
    #undef isinf
    #undef isnan
    #undef isnormal
    ...

And implements then all those undefined macros as function in the namespace std. This is at least true for gcc 6.1.1. That's why your compiler can't find isnan.

sv90
  • 522
  • 5
  • 11
  • 1
    I can compile both `std::isnan` and `isnan` on `gcc-6.1` on [godbolt](https://godbolt.org/g/eTRVmF) – Steve Lorimer Aug 24 '16 at 19:18
  • 1
    If it were problem with undef, the order of including would matter, but it doesn't matter – Anton Malyshev Aug 24 '16 at 20:53
  • 3
    @AntonMalyshev : Why would the order matter? `cmath` will almost always include `math.h`, which will have a header guard – it will always be included once, and only once. – ildjarn Aug 24 '16 at 21:12