Reputation: 181
I tried to print out the value of pi as defined in the library math.h
which on my system is
# define M_PI 3.14159265358979323846 /* pi */
with this simple code:
#include <stdio.h>
#include <math.h>
int main() {
long double a = M_PI;
printf("%.20Lf\n", a);
return 0;
}
what I get as output is
3.14159265358979311600
why is it different from that defined in the math.h
library?
Is there a way to reproduce the one defined in the library?
My manual says the type long double
extends the precision up to 24 digits. Shouldn't be enough to represent that number?
Upvotes: 2
Views: 11622
Reputation: 222679
Is there a way to reproduce the one defined in the library?
Since the value of π is not likely to change in the near future, it is okay to hardcode it. To do this, we may prefer to use C’s hexadecimal floating-point format because some C implementations are not good at converting decimal values to binary floating-point, and it is not clear we can rely on atanl
to be as accurate as possible. Here is π to 132 bits after the radix point:
0x3.243f6a8885a308d313198a2e03707344ap0L
Upvotes: 1
Reputation: 153456
why is it different from that defined in the math.h library?
double
constant
3.14159265358979323846
is a double
. To retain long double
precision, use an L
. That will get us closer ...
See also @Eric Postpischil
// # define M_PI 3.14159265358979323846 /* pi */
# define M_PI_L 3.14159265358979323846L /* pi */
// ^
Is there a way to reproduce the one defined in the library?
Not quite.
Binary nature of floating point
OP's long double
cannot represent every decimal encoded value exactly. Instead a binary one is used of the form: some_int*2exponent. On my machine the closest to 3.14159265358979323846 is 3.14159265358979323851...
int main() {
// 3.1415926535897932384626433832795...
# define M_PI_L 3.14159265358979323846L /* pi */
long double a = M_PI_L;
printf("%.21Lf epsilon\n", LDBL_EPSILON);
printf("%.21Lf local epsilon\n", a - nextafterl(a, 0.0));
printf("%.21Lf before\n", nextafterl(a, 0.0));
printf("3.14159265358979323846L source code\n");
printf("%.21Lf M_PI_L\n", a);
printf("%.21Lf after\n", nextafterl(a, a*2));
}
Sample output (spaces added for clarity)
0.000000000000000000 108 epsilon
0.000000000000000000 217 local epsilon
3.141592653589793238 296 before
3.141592653589793238 46L source code
3.141592653589793238 513 M_PI_L
3.141592653589793238 730 after
My manual says the type long double extends the precision up to 24 digits. Shouldn't be enough to represent that number?
"... up to 24 digits" may be misleading. Recall floating point numbers are overwhelmingly encoded as an integer times a power of 2. Thus the precision is a binary one. Depending on the region of FP values, the corresponding decimal precision wobbles between LDBL_DIG
and LDBL_DECIMAL_DIG
which is 18, 21 on my machine. I would expect the same with OP.
@DarkAtom idea is good. To typically get the machine's best machine pi :
// Perform this once
const long double pi = acosl(-1.0L);
An alternative is to specify with lots of digits, more than expected on any target.
// 50 digit pi 1 2345678901234567890123456789012345678901234567890
const long double pi = 3.1415926535897932384626433832795028841971693993751L;
Upvotes: 2
Reputation: 3161
The best precision for the mathematical constant π (pi) as provided by the implementation can be queried in a standard manner by calling acosl(-1);
:
const long double pi = acosl(-1.0L);
printf("%.20Lf\n", pi);
Since this approach has the additional overhead of performing a computation (whereas your approach uses a compile-time constant), it is recommended to place the result in a constant variable and use that for the entire program, performing the calculation only once. The advantage is that this is portable. This will compute π with the best available precision on the target platform and requires zero change in the code when switching platforms.
Conforming implementations are forbidden by the Standard to define the constant M_PI
, because implementation-defined reserved names must start with _
followed by a capital letter or another _
. If you want to use it, you have to define it yourself.
Upvotes: 2