C does not even have
M_PI standardized. C++ only added
std::numbers::pi very recently. Yes, the fact that this took so long does hint at some issues. Both languages have an upper bound to their precision of their floating point types (
long double) which are of fixed size. π is just a number and could be offered in
long double form. So, what are the 'issues' with adding π?
C does not even have
Both languages have an upper bound to their precision of their floating point types (
long double) which are of fixed size.
This does not appear to be true. The floating-point numbers types have lower bounds but not upper bounds for their bit-widths (see Wikipedia or Stack Overflow) and there weren't standardised floating-point types that require exact widths until C++23 (see Stack Overflow).
So, mandating a specific value for
M_PI would preclude implementations from using more precise values in implementations where more precision would be supported.
I suppose the spec could have said something like "there must be a constant
M_PI of type
double whose value is the closest representable value to the mathematical constant π". That would work as a specification, though it would be a bit unusual to specify a constant without giving it a literal value. It would also potentially preclude implementations like this one, which load whatever value of π the processor uses; the processor's value of π might load into a register with more bits of precision than a
double would normally have.
There is no deep reason. The answer is social:
The C standard does not specify any constant for π. We can only speculate on why, but ultimately, the set of things the C standard does and does not specify is somewhat arbitrary. We have no reason to believe this was an intentional omission rather than something nobody has bothered to specify.
POSIX specifies that
M_PI, but only if the appropriate feature test macro is set. MSVC requires
_USE_MATH_DEFINESbe defined before
math.his included. These were often used by C++ programmers, albeit non-portably.
A π constant is not difficult for programmers to define, so the absence of a portable way to access it was not a showstopper. Some programmers defined it themselves, others used definitions provided from third-party libraries like Boost.
Eventually, someone decided that relying on the non-portable definition of
M_PIor user definitions was sufficiently annoying that they took the time to write up a proposal to standardize math constants in the C++ standard library, and that was that.
Nowhere in the proposal is there any indication that there were any technical obstacles to its implementation. Indeed, it makes it sound quite as though it was an arbitrary omission that nobody had bothered to rectify:
C++ inherited from C a rich library of mathematical functions which continues to grow with every release. Amid all this abundance, there is a strange gap: none of the major mathematical constants is defined in the standard. This proposal is aimed to rectify this omission.
Standardizing anything in a language with as many users as C++ is slow and takes a lot of time, even if it’s fairly uncontroversial. Sometimes obvious inconveniences don’t get fixed because nobody cares enough to go through that process when easy workarounds are available. Perhaps they hope someone else will get around to it.
Eventually, someone did.
I can't find anything in the Rationale that specifically addresses mathematical constants, or the lack thereof, so I can only speculate.
- Other popular programming languages at the time didn't have a standard π constant. Pascal didn't. BASIC didn't. Even FORTRAN, the original language of scientific computing, didn't have a standard π constant yet. A lot of code from the era wrote
PI = 4 * atan(1)or equivalent to calculate it.
- When C was first standardized in 1989, hardware floating-point wasn't as ubiquitous as it is today. Floating-point coprocessors (like the Intel 8087 for IBM PCs) were an expensive extra used only by serious number-crunchers. Compilers at the time provided the option to require a coprocessor, to emulate floating-point instructions in software (slowly), or to just not link the floating-point functions at all (hence GCC separating
libcto this day). The perception was that floating point was something to avoid unless you absolutely needed it, so
<math.h>would be kept as minimalist as possible.
- The committee didn't want to waste time on arguments about which constants to define (π? e? φ? τ?) or what type they should have (
long double? All three?), and it was easier to just do nothing and leave this stuff up to the programmers.
There is no good way to write the exact value of PI in a C program without changing the compiler to add syntax for hex floating point constants; and writing them to lose no bits on conversion is tricky.
const long double pi = 3.141592653589793238462643; doesn't work on most compilers. The last few bits come out wrong.
However you can always get PI by doing
atan(1) * 4, and with no loss of precision if your runtime is any good. So there's just insufficient reason to compel the change to the compiler.
In comments on this answer it has been asked why we didn't end up with
extern const long double pi;. The primary reason is there are no other such constants. All of the constants are actually macros. The secondary reason is the C standard library is itself meant to be portable, and so it was between Unix systems. The hackery required to make such a declaration of pi work (that is, where just assigning it the decimal value wouldn't work, which is the origin of the problem) would invariably end up being non portable. And no, you can't do
const long double pi = atan(1) * 4; in the standard library. The no function calls in global initializers rule isn't arbitrary. There's simply no way to compile that.