tl;dr there is no mathematical constants in Standard C and C++.
Warning
I am not an adequate coder of C or C++, any information or thoughts provided here could have flaws. Moreover, I only use GNU Compiler Collection (GCC)1, some solutions may not be applicable. Please don’t hesitate to point out my mistakes.
[1] | With GCC 4.8.4 on Gentoo Linux. |
Contents
1 The error: M_PI undeclared
I’ve seen the following error many times, whenever I felt that I should code project foobar in Standard C, and every time it was thrown at me by gcc, I usually have to re-think what solution I should take. By re-think, I meant re-search on the Internet.
test.c:22:18: error: 'M_PI' undeclared (first use in this function)
printf("%f\n", M_PI);
2 Standards define no mathematical constants
The error message is pretty clear, apparently, M_PI is not declared as stated. The question is why do so many people use it and we get the error during the compilation.
A quick and simple answer is M_PI and other mathematics constants, such as M_E for Euler’s number, are not in Standard C and C++. From Trigonometric Functions in the GNU C Library’s manual:
The math library normally defines M_PI to a double approximation of pi. If strict ISO and/or POSIX compliance2 are requested this constant is not defined, but you can easily define it yourself:
According to Predefined Mathematical Constants also in the GNU C Library’s manual:
These constants come from the Unix98 standard and were also available in 4.4BSD; therefore they are only defined if _XOPEN_SOURCE=500, or a more general feature select macro, is defined. The default set of features includes these constants. See Feature Test Macros.
UNIX 98 is the Single UNIX Specification version 2, back in 1997, and 4.4BSD in 1993, these constants go back for at least three decades, no wonder we all use them.
It seems these commonly used mathematical constants never are part of the standards, they have never been defined ever since the first standard, C89. I wonder why? Maybe there is a principle to prevent them from being included, some MUST NOT or SHOULD NOT? Since I have never read the standard, I’d not know, but if there is one, please quote for me. Or, perhaps, math.h is laid out to be only the “declarations for math functions” as the first line of GNU C Library’s implementation states.
However, I am more curious about why no M_2PI — not to be confused with M_2_PI, — isn’t quite commonly used?
One more thing, it’s strange that Wikipedia has no mentions about these non-standard constants but only the mathematical functions, when lots of people are looking for a definition from math.h.
[2] | I don’t know why it says that, maybe it refers to older POSIX? Or I misread the POSIX specification as linked in this section. |
3 C++ on GNU/Linux
Although I have never coded in C++, while writing this post, I found out there is a strange behavior, the _GNU_SOURCE is defined as shown below:
% cpp -xc++ -std=c++98 -dD /dev/null | grep -B1 GNU_SOURCE
# 1 "<command-line>"
#define _GNU_SOURCE 1
$ clang -E -xc++ -std=c++98 -dM /dev/null | grep GNU_SOURCE
#define _GNU_SOURCE 1
As you can see in the issued command, I didn’t define the macro, _GNU_SOURCE, however, it showed up under the <command-line> section. The GNU C++ Library’s FAQ has an entry explaining it:
- _XOPEN_SOURCE and _GNU_SOURCE are always defined?
On Solaris, g++ (but not gcc) always defines the preprocessor macro _XOPEN_SOURCE. On GNU/Linux, the same happens with _GNU_SOURCE. (This is not an exhaustive list; other macros and other platforms are also affected.)
[…]
Since almost all the Linux distributions are with GNU tools and libraries, that is GNU/Linux, in this case, the GNU C Library, it’s unavoidable that if you code in C++, you have actually enabled GNU extensions. If you ain’t aware of it, you could be writing a code that’s not as portable as you may think.
Also on lwn.net, Jonathan Wakely (jwakely) posted on 2014-03-13:
To confuse things a bit, G++ unconditionally defines _GNU_SOURCE on systems using glibc, because the C++ standard library wants to be able to make use of GNU extensions, so uses the “give me everything” approach.
[…]
It’s been more than a year, if there is a fix, then I haven’t seen it. Considering it’s the GNU C Library, and I use Gentoo Linux, therefore I don’t expect to see the fix at least for a year from my end. Nonetheless, since I hardly code in C or C++, I don’t even bother to check.
4 Solutions
Note
Please be aware that it’s just for simplified writing, the code for defining or undefining appropriate macros are all in the source code level. You may, of course, choose to do it with C preprocessor’s options, such as -D and -U with cpp. It all depends what the best practice is for your project.
4.1 Switching to GNU dialects
Yes, if you switch to GNU dialects, for example, to -std=gnu99, your code may not conform to the Standard C99, and it may also not to be portable. But if none of those is an issue to you, then you might as well to make the switch, especially when you use a few of those constants.
A big block of constants definitions, no thanks.
4.2 Defining M_PI
However, if the conformance is important to your project, then defining all the constants you need could be the best solution. Ticker with other macros might not be good idea.
The value of is extracted from the GNU C Library’s math.h.
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
It also has one for the 128-bit IEEE quadruple-precision, long double:
#define M_PIl 3.141592653589793238462643383279502884L
As for C++,
-std= | code |
---|---|
c++98 |
|
c++03 | |
c++11 |
|
4.3 Computing
From Trigonometric Functions in the GNU C Library’s manual:
You can also compute the value of pi with the expression acos (-1.0).
I personally don’t like this approach, but only because of the initialization in C, you will need to do something like
double M_PI;
long double M_PIl
int
main(int argc, char *argv[])
{
M_PI = acos(-1.0);
M_PIl = acosl(-1.0);
/* ... */
}
Note that acosl is only available in C99 and C11, perhaps using preprocessor conditional would help, you could use literal if the function isn’t available, such as C89.
#if __STDC_VERSION__ >= 199901L
// ..
#endif
For C++,
-std= | code |
---|---|
c++98 |
|
c++03 | |
c++11 |
|
4.4 Defining _USE_MATH_DEFINES
_USE_MATH_DEFINES is quite a common solution I can see from search results, especially among Visual C/C++ related topics, MSDN also suggests this solution. However, with GCC, I need to undefine __STRICT_ANSI__ first.
C | C++ |
---|---|
|
|
4.5 Defining _XOPEN_SOURCE or _GNU_SOURCE
POSIX specifies those mathematical constants and you may also want to read about feature test macros. You can simply do the following,
C | C++ |
---|---|
|
|
Or defining _GNU_SOURCE, instead, which actually defines _XOPEN_SOURCE for you. It’s a superset of features of _XOPEN_SOURCE and some others plus GNU extensions, see GNU C Library’s features.h.
I tried to -D_POSIX_C_SOURCE=200809L, which didn’t work. GNU C Library’s math.h seems only allows _GNU_SOURCE and _XOPEN_SOURCE to have those constants defined. Either I misread POSIX.1-2008 standard or the library or GCC doesn’t do it right.
The bottom line is _XOPEN_SOURCE is enough. If you insist on using _GNU_SOURCE, I think you might as well as switching to GNU dialects.
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.