From: Dave Steffen on

Hi Folks,

I came across an annoying thing: the 'isnan' function isn't a
function, it's actually a macro (at least on my Linux system), defined
in math.h.

In principle, the notion was IIRC that #include <math.h> pulls the
contents of math.h into the global namespace (a la C), while <cmath>
pulls the contents into namespace std.

Of course, you can't pull a macro (nasty things) into namespaces at
all. Does the standard say anything about what #include <cmath> does
to macros (he asks, expecting the answer 'no).

What I'd like to do is arrange for 'isnan' to actually act like a
function, so I can provide overloaded versions to handle some
locally-grown numeric types. One possibility:

#include <cmath>
namespace detail {
bool isNaN(double d) { return isnan(d); }
}

#undef isnan
namespace myNameSpace{
bool isnan(double d) { return detail::isNaN(d); }
}

// carry on using isnan on doubles and providing overloads for other
// things

Comments? Notions? Half-baked ideas? Thanks!

--
Dave Steffen - Software Engineer 4
Numerica Corporation (www.numerica.us <http://www.numerica.us/> )


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Bob on
Dave Steffen wrote:

>
> Hi Folks,
>
> I came across an annoying thing: the 'isnan' function isn't a
> function, it's actually a macro (at least on my Linux system), defined
> in math.h.

isnan() is also a macro, according to the 1999 C standard. I haven't
checked if that is so in the 1989/90 C standard.

>
> In principle, the notion was IIRC that #include <math.h> pulls the
> contents of math.h into the global namespace (a la C), while <cmath>
> pulls the contents into namespace std.

That clearly does not work with macros, as macros do not respect scope.
Once the preprocessor has done it's job, the macro no longer exists
as far as the compiler is concerned.

>
> Of course, you can't pull a macro (nasty things) into namespaces at
> all. Does the standard say anything about what #include <cmath> does
> to macros (he asks, expecting the answer 'no).
>
> What I'd like to do is arrange for 'isnan' to actually act like a
> function, so I can provide overloaded versions to handle some
> locally-grown numeric types. One possibility:
>
> #include <cmath>
> namespace detail {
> bool isNaN(double d) { return isnan(d); }
> }
>
> #undef isnan
> namespace myNameSpace{
> bool isnan(double d) { return detail::isNaN(d); }
> }
>
> // carry on using isnan on doubles and providing overloads for other
> // things
>
> Comments? Notions? Half-baked ideas? Thanks!


You actually have a few issues here, not least of which is that floating
point types are not actually required to be able to represent a NaN
value (eg if your compiler does not support IEEE formats).

In any event, the std::numeric_limits class (within <limits> includes
facilities related to detecting if a "real" type supports NaN. Using
a similar approach you would be able to provide your IsNaN() function
as a template, which is specialised appropriately for your types.

More generally, a common practical reason for wanting to detect
NaN's is that you have numerical code that can generate them. I would
suggest one option is to modify that code so it detects the conditions
that would *result* in a NaN and give an error return code of some
form.

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Alberto Ganesh Barbati on
Dave Steffen ha scritto:
> Hi Folks,
>
> I came across an annoying thing: the 'isnan' function isn't a
> function, it's actually a macro (at least on my Linux system), defined
> in math.h.

Correct, C99 requires isnan() to be a macro.

> In principle, the notion was IIRC that #include <math.h> pulls the
> contents of math.h into the global namespace (a la C), while <cmath>
> pulls the contents into namespace std.

That is incorrect.

First of all, the standard library of C++03 makes reference to the C
library of C90, not of C99. I don't have C90 at hand, but I guess
isnan() was not present in C90. So, in order for this discussion to make
sense, we have to consider the next revision of C++, which makes
reference to C99 and mentions isnan() explicitly.

Please notice that in C++ <math.h> is defined in terms of <cmath> and
not viceversa. This means that the <math.h> in C++ may be different from
the <math.h> in C, in the sense that they do not necessarily define the
same things (how this is achieved on a shared C/C++ implementation,
whether by using two different files, by relying on the pre-processor or
by any other mean, is unspecified).

Actually, according to latest draft of C++0X, <cmath> shall define (in
namespace std) several things with the same meaning as in the C99
<math.h>, but *not* all of it and also adds several things. In
particular, <cmath> shall *not* define a macro named isnan() nor any of
the other is*() macros. On the contrary, isnan() is defined to be a
template.

> Of course, you can't pull a macro (nasty things) into namespaces at
> all. Does the standard say anything about what #include <cmath> does
> to macros (he asks, expecting the answer 'no).

C++0X (which is not yet a standard!) says explicitly that <cmath> shall
not define is*() as macros but as templates in section 26.7/12:

"The templates defined in <cmath> replace the C99 macros with the same
names. The templates have the following declarations:
namespace std {
template <class T> bool signbit(T x);
template <class T> int fpclassify(T x);
template <class T> bool isfinite(T x);
template <class T> bool isinf(T x);
template <class T> bool isnan(T x);
template <class T> bool isnormal(T x);
template <class T> bool isgreater(T x, T y);
template <class T> bool isgreaterequal(T x, T y);
template <class T> bool isless(T x, T y);
template <class T> bool islessequal(T x, T y);
template <class T> bool islessgreater(T x, T y);
template <class T> bool isunordered(T x, T y);
} // namespace std"

> What I'd like to do is arrange for 'isnan' to actually act like a
> function, so I can provide overloaded versions to handle some
> locally-grown numeric types. One possibility:
>
> #include <cmath>
> namespace detail {
> bool isNaN(double d) { return isnan(d); }
> }
>
> #undef isnan
> namespace myNameSpace{
> bool isnan(double d) { return detail::isNaN(d); }
> }
>
> // carry on using isnan on doubles and providing overloads for other
> // things
>
> Comments? Notions? Half-baked ideas? Thanks!
>

I don't know the rationale of providing templates rather than overloads.
Anyway, C++0X achieves your goal without messing with macros.

HTH,

Ganesh

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: P.J. Plauger on
"Alberto Ganesh Barbati" <AlbertoBarbati(a)libero.it> wrote in message
news:vOyfj.214763$U01.1393855(a)twister1.libero.it...

> Dave Steffen ha scritto:
>> Hi Folks,
>>
>> I came across an annoying thing: the 'isnan' function isn't a
>> function, it's actually a macro (at least on my Linux system), defined
>> in math.h.
>
> Correct, C99 requires isnan() to be a macro.
>
>> In principle, the notion was IIRC that #include <math.h> pulls the
>> contents of math.h into the global namespace (a la C), while <cmath>
>> pulls the contents into namespace std.
>
> That is incorrect.

It's correct, just not complete.

> First of all, the standard library of C++03 makes reference to the C
> library of C90,

No, C95.

> not of C99. I don't have C90 at hand, but I guess
> isnan() was not present in C90. So, in order for this discussion to make
> sense, we have to consider the next revision of C++, which makes
> reference to C99 and mentions isnan() explicitly.

Or you can consider TR1, which already includes isnan a la C99.
It's not a required standard, but it is a widely supported Technical
Report. And most of it (including the isnan bit) has already been
voted into C++0X.

> Please notice that in C++ <math.h> is defined in terms of <cmath> and
> not viceversa.

In principle, but rarely in practice. C++0X has already accepted
current practice as future principle.

> This means that the <math.h> in C++ may be different
> from
> the <math.h> in C, in the sense that they do not necessarily define the
> same things (how this is achieved on a shared C/C++ implementation,
> whether by using two different files, by relying on the pre-processor or
> by any other mean, is unspecified).

Aside from some tinkering with a handful of function declarations, the
primary difference is that C++ disallows masking macros for C functions.

> Actually, according to latest draft of C++0X, <cmath> shall define (in
> namespace std) several things with the same meaning as in the C99
> <math.h>, but *not* all of it and also adds several things. In
> particular, <cmath> shall *not* define a macro named isnan() nor any of
> the other is*() macros. On the contrary, isnan() is defined to be a
> template.
>
>> Of course, you can't pull a macro (nasty things) into namespaces at
>> all. Does the standard say anything about what #include <cmath> does
>> to macros (he asks, expecting the answer 'no).
>
> C++0X (which is not yet a standard!) says explicitly that <cmath> shall
> not define is*() as macros but as templates in section 26.7/12:

Yes, this is an analytic continuation of the "no masking macros" rule
from C++98.

> "The templates defined in <cmath> replace the C99 macros with the same
> names. The templates have the following declarations:
> namespace std {
> template <class T> bool signbit(T x);
> template <class T> int fpclassify(T x);
> template <class T> bool isfinite(T x);
> template <class T> bool isinf(T x);
> template <class T> bool isnan(T x);
> template <class T> bool isnormal(T x);
> template <class T> bool isgreater(T x, T y);
> template <class T> bool isgreaterequal(T x, T y);
> template <class T> bool isless(T x, T y);
> template <class T> bool islessequal(T x, T y);
> template <class T> bool islessgreater(T x, T y);
> template <class T> bool isunordered(T x, T y);
> } // namespace std"
>
>> What I'd like to do is arrange for 'isnan' to actually act like a
>> function, so I can provide overloaded versions to handle some
>> locally-grown numeric types. One possibility:
>>
>> #include <cmath>
>> namespace detail {
>> bool isNaN(double d) { return isnan(d); }
>> }
>>
>> #undef isnan
>> namespace myNameSpace{
>> bool isnan(double d) { return detail::isNaN(d); }
>> }
>>
>> // carry on using isnan on doubles and providing overloads for other
>> // things
>>
>> Comments? Notions? Half-baked ideas? Thanks!
>>
>
> I don't know the rationale of providing templates rather than overloads.

There's a rather widespread practice of templatizing on floating-point
types. See, e.g. complex.

> Anyway, C++0X achieves your goal without messing with macros.

Yes.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Alberto Ganesh Barbati on
P.J. Plauger ha scritto:
> "Alberto Ganesh Barbati" <AlbertoBarbati(a)libero.it> wrote in message
> news:vOyfj.214763$U01.1393855(a)twister1.libero.it...
>
>> Please notice that in C++ <math.h> is defined in terms of <cmath> and
>> not viceversa.
>
> In principle, but rarely in practice. C++0X has already accepted
> current practice as future principle.

I meant that the standard first defines what <cmath> is and then defines
<math.h> in terms of the former (in D.5 to be precise). Once you have
defined what they should do, how the implementation achieves that,
either by having <cmath> include <math.h> or viceversa or any other
mean, is left unspecified.

>>
>> I don't know the rationale of providing templates rather than overloads.
>
> There's a rather widespread practice of templatizing on floating-point
> types. See, e.g. complex.

Hmmm... Then why are all the other mathematical functions not templated
too? I mean, if what you say was the only reason, we should also have
template versions of sin(), cos() and abs() or at least of all the new
functions introduced in C++0X, if backward compatibility is a concern.

BTW, while looking at the draft, I realized that function nan() is
provided only in the double "flavor". This means it's impossible to get
the functionalities of nanf() and nanl(). Notice that all three
functions take a const char* argument so they are unsuitable for
overloading. std::numeric_limits<> is not a perfect replacement in this
case also because of the const char* argument. In this case, it would
make perfect sense to define nan() as a template, for example like this:

template <class T = double>
T nan(const char*);

Any idea why this wasn't considered?

Ganesh

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]