180

There is a rather silly problem with the number pi in C and C++. As far as I know M_PI defined in math.h is not required by any standard.

New C++ standards introduced a lot of complicated math in the standard library - hyperbolic functions, std::hermite and std::cyl_bessel_i, different random number generators and so on and so forth.

Did any of the 'new' standards bring in a constant for pi? If not - why? How does all this complicated math work without it?

I am aware of similar questions about pi in C++ (they are several years and standards old); I would like to know the current state of the problem.

I am also very interested in why oh why C++ still doesn't have a pi constant but has a lot of more complicated math.

I know that I can define pi myself as 4*atan(1) or acos(1) or double pi = 3.14;. Sure. But why do I still have to do it? How do standard math functions work without pi?

cigien
  • 50,328
  • 7
  • 37
  • 78
Amomum
  • 5,447
  • 5
  • 27
  • 54
  • You note the existence of old questions such as [Best platform independent pi constant?](https://stackoverflow.com/q/21867617/1708801). If you worry they are out of date you could always set a bounty on one of them asking for answers based on C++17 etc... Then all the answers would be in one place. Why is still a good question but perhaps this should focus on why and asking for up to date should be a bounty on existing questions. – Shafik Yaghmour Apr 13 '18 at 05:48
  • I think it may worth adding new answers since C++20 added a pi constant as far as I know – Guillaume Racicot Aug 05 '19 at 16:18
  • @GuillaumeRacicot i updated the question. Not sure if we should address C++20 since it's not officially out yet. – Amomum Aug 05 '19 at 16:22
  • @GuillaumeRacicot: It’s a bit late to add one… – Davis Herring Oct 09 '19 at 22:16

6 Answers6

115

Up to and including C++17 pi is not a constant introduced into the language, and it's a pain in the neck.

I'm fortunate in that I use boost and they define pi with a sufficiently large number of decimal places for even a 128 bit long double.

If you don't use Boost then hardcode it yourself. Defining it with a trigonometric function is tempting but if you do that you can't then make it a constexpr. The accuracy of the trigonometric functions is also not guaranteed by any standard I know of (cf. std::sqrt), so really you are on dangerous ground indeed relying on such a function.

There is a way of getting a constexpr value for pi using metaprogramming: see http://timmurphy.org/2013/06/27/template-metaprogramming-in-c/


From C++20 some good news. There is a defininition for pi. C++20 adds some mathematical constants in <numbers>. For example std::numbers::pi is a double type.

Reference: https://en.cppreference.com/w/cpp/numeric/constants

Bathsheba
  • 220,365
  • 33
  • 331
  • 451
  • 1
    As someone suggested, there's also `const long double PI = acosl(-1.0L);`. – Lundin Apr 11 '18 at 15:16
  • 10
    @Lundin: But that can't be a `constexpr` unfortunately, which is why I say "Defining it with a trig function is a pain " – Bathsheba Apr 11 '18 at 15:16
  • 9
    Why does it matter? Pi is a constant, not subject to change or anything implementation-specific. Just write out the value (in a const object if you like) to the number of places significant for `double` (or some ridiculous number if you care about hypothetical really-long long doubles). – R.. GitHub STOP HELPING ICE Apr 11 '18 at 15:47
  • 10
    @R..The problem I have with that is what appears ridiculous now can become perfectly sane in 20 years time or so. (Bill Gates' 640k springs to mind). I trust Boost to keep up with architecture evolution. – Bathsheba Apr 11 '18 at 15:48
  • Memorizing the value of pi is much easier than memorizing the formula. (although I have not attempted to memorize the formula at all...) – user202729 Apr 11 '18 at 16:43
  • 22
    @Bathsheba But is that actually relevant? As far as I understand, [~40 digits of pi are enough precision, and will always be enough precision, for any calculation to achieve sub-atomic precision on the scale of the whole Universe.](https://physics.stackexchange.com/a/9631/147) – Konrad Rudolph Apr 11 '18 at 16:47
  • @KonradRudolph - I thought it was in the 80s: based on the log of the number of cubes of 1 Planck length making up the observable universe. I wonder if anyone has a closer number? And I couldn’t help noticing that you’ve edited your comment; doubling the number! If you double it again you’re at where I’m at! – Bathsheba Apr 11 '18 at 16:53
  • 1
    @Bathsheba I only vaguely remembered, and updated the number when I found a reference. – Konrad Rudolph Apr 11 '18 at 16:55
  • 2
    @KonradRudolph - which is a pitiful answer indeed: plenty of physics happens on a sub-proton scale. For now my Planck length cube argument holds and the quality of SO answers is evidently better than the ones on the fizzix site. – Bathsheba Apr 11 '18 at 16:57
  • 5
    @Bathsheba I don’t think a lot of physics (or *any*, actually) requires simultaneous precision at sub-proton and macroscopic (Universe) scale. In other words, the dynamic range in that (admittedly, not well written) answer is already exaggerated for all practical purposes. That said, I’m open to be told otherwise. – Konrad Rudolph Apr 11 '18 at 17:00
  • 3
    @KonradRudolph: You’re absolutely correct but C++ prides itself on being a general purpose language, so if you need unusual floating point accuracy then it ought to be able to provide it. A lot of physics gets bogged down with the fact that Newton’s G is known to woeful precision. – Bathsheba Apr 11 '18 at 17:03
  • 3
    How much need would there be for pi, if the standard library included a set of trig functions measured angles in units of 2pi radians? While there are a few purposes involving derivatives, integrals, and differential equations, for which radians are useful, for most purposes involving actual angles they're just about the worst unit imaginable. – supercat Apr 11 '18 at 17:15
  • 3
    @supercat: it pops up all over the place. Probability, financial mathematics, solutions using complex number libraries being some applications. C++ is a general purpose language and, as such, IMHO should have pi in its standard. – Bathsheba Apr 11 '18 at 17:18
  • 3
    @Bathsheba: Yeah, I guess radians end up being a natural unit for some kinds of operations on complex numbers because of Euler's equality, but it still irks me that there aren't any trig functions that are intended for use with actual *angles*. Having a graphics "rotate" transform use radians, for example, strikes me as just plain dumb, since likely as not the angle is going to be some multiple of pi/4, but rotating four times by pi/4 doesn't quite yield the original orientation. – supercat Apr 11 '18 at 17:30
  • 2
    @supercat You should be using quaternions for rotations anyway. – JAB Apr 11 '18 at 21:18
  • 11
    @KonradRudolph: A high-precision *pi* matters if implementing range-reduction. For example, x86/x87's internal Pi constant (full 64-bit mantissa) leads to a worst-case error for small inputs to the `fsin` instruction of ["about 1.37 quintillion units in the last place, leaving fewer than four bits correct"](https://randomascii.wordpress.com/2014/10/09/intel-underestimates-error-bounds-by-1-3-quintillion/), and it's even worse for large inputs where range-reduction wraps around multiple times. This is somewhat tangential to constants used for `long double` in C++, but neat anyway. – Peter Cordes Apr 12 '18 at 00:38
  • 2
    @KonradRudolph: Numerical evaluation of functions and constants requiring 5000 decimal digits has been used with the PSQL algorithm to deduce insights into theoretical physics and reduce evaluation of multidimensional integrals to compile-time constants. See David Bailey's work on this. Boost.math computes pi dynamically unless it's in a list of precomputed types; this would be a requirement for any C++ standard value of pi. – user14717 Apr 12 '18 at 10:19
  • As a simple experiment, plot `sin(pi*x)` with pi specified to 16 digits for double. Watch the zeros of sin drift by `x * epsilon()`. It would be good to have `sin_pi()`, etc. but sometimes you just need pi because your problem doesn't present as x*pi. You might well need higher than working precision pi to prevent this aliasing. This could be implemented with constexpr expression templates for fundamental types. The pi object for fundamental types might be an unevaluated sum of the float type - a polynomial in epsilon() if you will. – emsr Apr 12 '18 at 14:34
  • As an aside, I think all the basic math functions could have constexpr noexcept versions if the have error code returns (no write to global, and invariably ignored, `errno`). `double sin(double x, error_code& ec)`; and `auto [lgval, lgsign] = lgamma(x, ec);`, etc. – emsr Apr 12 '18 at 14:39
  • If you absolutely need precision up to a particular number of digits, why are you using floating point numbers and not fixed? – Cubic Apr 13 '18 at 10:16
  • @Cubic: Floating point is very fast, and, so long as you know how they behave (error accumulation &c.) they are the perfect tool for many physical and financial modelling applications. – Bathsheba Apr 13 '18 at 11:30
  • 2
    Precision of π in Boost is actually in a funny situation: on the one hand we have this constant you cite, on the other we have [this](https://stackoverflow.com/a/30755358/673852), which is due to a [7-year-old bug](https://svn.boost.org/trac10/ticket/6893). – Ruslan Nov 23 '18 at 07:08
  • Nobody is fortunate to use Boost – BrandonL Aug 06 '19 at 23:19
34

Up to C++20, no, none of the standards introduced the constant that would represent the number pi (π). You can approximate the number in your code:

constexpr double pi = 3.14159265358979323846;

Other languages such as C# have the constant declared in their libraries.

Update: Starting with the C++20, there indeed is a pi constant declared inside the <numbers> header. It is accessed via: std::numbers::pi.

Ron
  • 13,116
  • 3
  • 25
  • 45
  • 6
    You might want to add `inline` for C++17+. – Deduplicator Apr 11 '18 at 15:05
  • 8
    Ta. Have an upvote, but note that your definition is still vulnerable to imprecision with platforms with different definitions of `double`. C# has it easy since the `double` type is fixed. If I were on the C++ standards committee I'd propose something like `std::constants::pi` – Bathsheba Apr 11 '18 at 15:15
  • 12
    @Deduplicator isn't constexpr implicitly inline as well...? – Krupip Apr 11 '18 at 15:32
  • 1
    @Bathsheba: `double` is IEEE double on any reasonable implementation (and any where C conforms to Annex F; I'm not sure if C++ has an analogue but it should). There is no good reason to treat `double` as a type whose precision could vary. Use `long double` if you want that. – R.. GitHub STOP HELPING ICE Apr 11 '18 at 15:48
  • 4
    @R. Fair enough, although you should static assert on `std::numeric_limits::is_iec559;` in that case. Which, I confess, is what I have in my "master header". Note that formally you need to check all the floating point types separately. Just because one is IEEE754 doesn't mean they all are. – Bathsheba Apr 11 '18 at 15:51
  • 1
    @snb: For functions, and since C++17 which allowed it and introduced inline-variables, sure, `constexpr` implies `inline`. But **namespace-level declarations didn't suddenly change their meaning**. – Deduplicator Apr 11 '18 at 16:25
  • 1
    @snb `constexpr` *does not imply* `inline` for namespace scope variable [dcl.constexpr]: *A function or **static data member** declared with the constexpr specifier is implicitly an inline function or variable* – Oliv Apr 11 '18 at 17:30
  • What about something like `0x3.243F6A8885A308D3p+0` which would make it absolutely clear how many bits of precision you're giving? – Daniel Schepler Apr 12 '18 at 18:56
  • 2
    @DanielSchepler So, what is that "number" supposed to be? I didn't know there are double numbers with 16 base. – BЈовић Apr 13 '18 at 09:40
31

As others said there is no std::pi but if you want precise PI value you can use:

constexpr double pi = std::acos(-1);

This assumes that your C++ implementation produces a correctly-rounded value of PI from acos(-1.0), which is common but not guaranteed.

It's not constexpr, but in practice optimizing compilers like gcc and clang evaluate it at compile time. Declaring it const is important for the optimizer to do a good job, though.

KPCT
  • 3,815
  • 2
  • 16
  • 34
  • 3
    This is dangerous because the `acos()` function has an infinite slope at `x = -1`. Consequently, this method relies on the `acos()` implementation to basically explicitly catch the case of a precise `-1` argument, and return the correct constant directly. Better use something like `4*atan(1)` which is mathematically much more robust (well-behaved slope at `x = 1` and multiplication by 4 is always precise with floating point math). – cmaster - reinstate monica Aug 05 '19 at 16:17
  • 2
    We are not allowed to use `std::acos` in a constant expression. clang reports this as error. Kindly note that this is a **non-conforming extension** and should eventually be fixed in gcc. Kindly refer to this [answer](https://stackoverflow.com/questions/32814678/constexpr-compile-error-using-stdacos-with-clang-not-g) for more details. – badola Sep 12 '19 at 06:31
29

M_PI is defined by "a standard", if not a language standard: POSIX with the X/Open System Interfaces extension (which is very commonly supported and required for official UNIX branding).

C++20, meanwhile, does have such constants (merged in the last round of C++20 features). Specifically, there is both std::numbers::pi (of type double) and a variable template that you can use if you want a different floating point type, e.g. std::numbers::pi_v<float>.

Davis Herring
  • 24,173
  • 3
  • 25
  • 55
  • Feel free to reword my edit as you see fit - I just wanted to add the actual spelling of the new constants in here for posterity. – Barry Oct 09 '19 at 15:30
12

It is not obviously a good idea because there is no obvious type with which define pi that is universally applicable across domains.

Pi is, of course, an irrational number so it cannot be correctly represented by any C++ type. You might argue that the natural approach, therefore, is to define it in the largest floating point type available. However, the size of the largest standard floating point type long double is not defined by the C++ standard so the value of the constant would vary between systems. Worse, for any program in which the working type was not this largest type, the definition of pi would be inappropriate since it would impose a performance cost on every use of pi.

It is also trivial for any programmer to find the value of pi and define their own constant suitable for use, so it does not provide any great advantage to include it in the maths headers.

Jack Aidley
  • 17,045
  • 6
  • 35
  • 66
  • 10
    C++14 gave us variable templates. Isn't it what they are for? – Amomum Apr 11 '18 at 15:43
  • 21
    spoken like a true committee member, talk about all the ways it can't be done despite clear path forward being presented. [See the suprisingly relevant example Variable Templates](http://en.cppreference.com/w/cpp/language/variable_template). I don't think your answer is bad, but I'm sure it wouldn't help Bjarne Stroustrup's eternal depression over the regret of handing control of C++'s future to a very indecisive committee. – Krupip Apr 11 '18 at 15:48
  • 4
    @snb I agree that making `pi` a polymorphic constant is the clear path forward – in a language with Hindley-Milner type inference. In Haskell, we've always had `pi :: Floating a => a`, so that `pi` would automatically have the value `3.1415927` in a `Float` context, `3.141592653589793` in a `Double` context, and `π` in an symbolic-computation context. But would people really like having to explicitly instantiate the template parameter? Seems a bit awkward, especially if a fixed `long double` implementation would give identical results in most applications. – leftaroundabout Apr 11 '18 at 19:59
  • There are several platform independent [methods laid out here](https://stackoverflow.com/q/21867617/1708801) ... so while it would be nice to have a standardized one we do have options. – Shafik Yaghmour Apr 11 '18 at 22:21
  • Well, we could also have a new empty trivial type `__exposition_pi_t` with implicit conversion to all types of numbers. – Deduplicator Apr 11 '18 at 22:30
  • 2
    @leftaroundabout I believe writing `auto a = pi;` is completely fine, certainly more readable then notorious `4*atan(1)` – Amomum Apr 12 '18 at 08:53
  • @Amomum yeah... people would probably still use monomorphic shorthands locally to de-clunk mathematical formulas, but I agree that `auto pi = std::pi` looks pretty clean. – leftaroundabout Apr 12 '18 at 09:11
  • I might argue that defining M_PI as a decimal constant to a "sufficient" number of digits would work nicely, without any of the problems you mention. Or, if typing *is* somehow a problem, then also define M_PI_F and M_PI_LD (with the latter specified for 128-bit implementations). – Chromatix Apr 12 '18 at 20:49
  • 1
    Ugh, I downvoted this by misclick and now I can't undo it. Sorry. This deserves a +1 for a pretty good explanation of the committee's reasoning for why it hasn't been added, even if I personally think the reasoning is fundamentally flawed for the reasons people have already pointed out. – Fund Monica's Lawsuit Apr 14 '18 at 05:49
  • They could easily have added a function `std::pi()` that is guaranteed to be defined for `T` being every possible floating. I'm not so familiar with the new-fangled `constexpr` idea, but presumably the function could be marked as that too. – Arthur Tacca May 29 '19 at 20:44
-1

Edited - To remove the term necessary, because it proved controversial. It is too much of an absolute term.

C++ is a large and complex language, for that reason the Standards Committee only include things which are strongly required. As much as possible is left to non-language standard libraries... like Boost.
boost::math::constants

Tiger4Hire
  • 303
  • 1
  • 7
  • 29
    Sure, `std::hermite` and `std::cyl_bessel_i` and `std::cosh` and `std::mersenne_twister_engine` and `std::ranlux48` and `std::cauchy_distribution` and `std::assoc_laguerre` and `std::beta` all were absolutely necessary, we all use them every day! – Amomum Apr 13 '18 at 09:24
  • 2
    I'm pretty sure you couldn't get even the committee itself to sign off on the idea that everything they vote in is "absolutely necessary". It's not about necessity but about adding value to the language -- almost *nothing* in the standard library is necessary to write programs, and for that matter most of the core language can be tossed out as well (if you don't mind working in a Turing tarpit, that is). The value of the inclusion of a constant for pi can be debated, but the idea that it's not in there because it's just not necessary doesn't hold water. – Jeroen Mostert Apr 13 '18 at 09:38
  • Don't complain at me, I'm just quoting the standards committee. There were long open discussions about how much of boost should be included in the C++11 standard. The reason why such a small subset made it is because the compiler writers complained about how much testing was involved. Therefore, if something is in the standards, it's there because someone thought it necessary to standardise it. Just because you don't know why, doesn't mean there was no reason. – Tiger4Hire Apr 13 '18 at 10:11
  • 1
    @Tiger4Hire I'm sure there is reason for everything, I just can't understand why constant for pi was not added when a lot of more complex things were. Constant is easy to write with variable templates and won't require a lot of testing from compiler writers. – Amomum Apr 13 '18 at 14:33
  • @Amomum : Yes, adding pi seems like a small overhead for a big win. Mind, personally I'd prefer to see std::network before std::math::constants. – Tiger4Hire Apr 13 '18 at 16:25