A single/double/extended-precision floating-point representation of Pi is accurate up to how many decimal places?
#include <stdio.h>
#define E_PI 3.1415926535897932384626433832795028841971693993751058209749445923078164062
int main(int argc, char** argv)
{
long double pild = E_PI;
double pid = pild;
float pif = pid;
printf("%s\n%1.80f\n%1.80f\n%1.80Lf\n",
"3.14159265358979323846264338327950288419716939937510582097494459230781640628620899",
pif, pid, pild);
return 0;
}
Results:
[quassnoi #] gcc --version
gcc (GCC) 4.3.2 20081105 (Red Hat 4.3.2-7)
[quassnoi #] ./test
3.14159265358979323846264338327950288419716939937510582097494459230781640628620899
3.14159274101257324218750000000000000000000000000000000000000000000000000000000000
^
3.14159265358979311599796346854418516159057617187500000000000000000000000000000000
^
3.14159265358979311599796346854418516159057617187500000000000000000000000000000000
^
0000000001111111
1234567890123456
E_PI
must have L
suffix to get long double precision, otherwise it'll stuck at double precision –
Bellow When I examined Quassnoi's answer it seemed suspicious to me that long double
and double
would end up with the same accuracy so I dug in a little. If I ran his code compiled with clang I got the same results as him. However I found out that if I specified the long double
suffix and used a literal to initialize the long double it provided more precision. Here is my version of his code:
#include <stdio.h>
int main(int argc, char** argv)
{
long double pild = 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899L;
double pid = pild;
float pif = pid;
printf("%s\n%1.80f\n%1.80f\n%1.80Lf\n",
"3.14159265358979323846264338327950288419716939937510582097494459230781640628620899",
pif, pid, pild);
return 0;
}
And the results:
3.14159265358979323846264338327950288419716939937510582097494459230781640628620899
3.14159274101257324218750000000000000000000000000000000000000000000000000000000000
^
3.14159265358979311599796346854418516159057617187500000000000000000000000000000000
^
3.14159265358979323851280895940618620443274267017841339111328125000000000000000000
^
6 places and 14 places.1 place is over 0 for the 3, and the last place although stored can't be considered as a precision point.
And sorry but I don't know what extended means without more context. Do you mean C#'s decimal?
In the x86 floating-point unit (x87) there are instructions for loading certain floating point constants. "fldz" and "fld1" load 0.0 and 1.0 onto the stack top "st" (aka "st(0)") for example. Another is "fldpi".
All these values have a mantissa that's 64 bits long which translates into close to 20 decimal digits. The 64 bits are possible through the 80-bit tempreal floating point format used internally in the x87. The x87 can load tempreals from and store them to 10 byte memory locations as well.
Accuracy of a floating-point type is not related to PI or any specific numbers. It only depends on how many digits are stored in memory for that specific type.
In case of IEEE-754 float
uses 23 bits of mantissa so it can be accurate to 23+1 bits of precision, or ~7 digits of precision in decimal. Regardless of π, e, 1.1, 9.87e9... all of them is stored with exactly 24 bits in a float. Similarly double
(53 bits of mantissa) can store 15~17 decimal digits of precision.
double
and it will be accurate up to the 15th decimal (at least for the gcc that comes with Ubuntu 18, running on an intel core i5 --- I believe it's mapped to IEEE-754). You store 1000*pi, and it will be accurate up to the 12th decimal. –
Intricacy 1000*pi
and got a slightly less accurate result, that doesn't mean the precision was reduced. You got it wrong because you don't understand what "significand" is, which isn't counted after the radix point. In fact 1000*pi lose only 1 digit of precision and is still correct to the 15th digit of significand, not 12. You're also confusing between 'precision' and 'accuracy'? –
Bellow * EDIT: see this post for up to date discussion: Implementation of sinpi() and cospi() using standard C math library *
The new math.h functions __sinpi()
and __cospi()
fixed the problem for me for right angles like 90 degrees and such.
cos(M_PI * -90.0 / 180.0) returns 0.00000000000000006123233995736766
__cospi( -90.0 / 180.0 ) returns 0.0, as it should
/* __sinpi(x) returns the sine of pi times x; __cospi(x) and __tanpi(x) return
the cosine and tangent, respectively. These functions can produce a more
accurate answer than expressions of the form sin(M_PI * x) because they
avoid any loss of precision that results from rounding the result of the
multiplication M_PI * x. They may also be significantly more efficient in
some cases because the argument reduction for these functions is easier
to compute. Consult the man pages for edge case details. */
extern float __cospif(float) __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_NA);
extern double __cospi(double) __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_NA);
extern float __sinpif(float) __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_NA);
extern double __sinpi(double) __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_NA);
extern float __tanpif(float) __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_NA);
extern double __tanpi(double) __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_NA);
__sinpi()
and __cospi()
are definitely not standard functions. It's easy to see as they have the __
prefix. Searching for them mostly returns result for macOS and iOS. This question said that it's been added by Apple Implementation of sinpi() and cospi() using standard C math library, and the man page also says that it's in OSX –
Bellow In C++20 there was introduced <format>
header, which is able to print with given type full precision without unnecessary decimal places. Unfortunatelly, this functionality is missing even in compilers supporting C++20, e.g. gcc 12.
Thus it is necessary to use fmt library https://fmt.dev/latest/index.html I have extracted it to fmt
dir.
Now create main.cpp
#include <iostream>
//#include <format>
#define FMT_HEADER_ONLY
#include <fmt/format.h>
int main(int argc, char** argv) {
long double pild = std::numbers::pi_v<long double>;
long double twopild = pild * 2.0L;
long double fourpisquaredld = pild * pild * 4.0L;
double pid = pild;
double twopid = twopild;
double fourpisquaredd = fourpisquaredld;
float pif = pild;
float twopif = twopild;
float fourpisquaredf = fourpisquaredld;
std::cout << fmt::format("PIL={}\n", pild);
std::cout << fmt::format("TWOPIL={}\n", twopild);
std::cout << fmt::format("FOURPISQUAREDL={}\n", fourpisquaredld);
std::cout << fmt::format("PID={}\n", pid);
std::cout << fmt::format("TWOPID={}\n", twopid);
std::cout << fmt::format("FOURPISQUAREDD={}\n", fourpisquaredd);
std::cout << fmt::format("PIF={}\n", pif);
std::cout << fmt::format("TWOPIF={}\n", twopif);
std::cout << fmt::format("FOURPISQUAREDF={}\n", fourpisquaredf);
return 0;
}
Compiled using gcc 12.
g++ -I fmt/include -std=c++20 main.cpp
Output
PIL=3.1415926535897932385
TWOPIL=6.283185307179586477
FOURPISQUAREDL=39.478417604357434478
PID=3.141592653589793
TWOPID=6.283185307179586
FOURPISQUAREDD=39.47841760435743
PIF=3.1415927
TWOPIF=6.2831855
FOURPISQUAREDF=39.478416
Interestingly in Python you receive full precision print by conversion to string so we can do e.g.
import numpy as np
print("%s"%(float(np.pi)))
obtaining 3.141592653589793 since float in Python is represented as double precision number.
Print and count, baby, print and count. (Or read the specs.)
World of PI have PI to 100,000,000,000 digits, you could just print and compare. For a slightly easier to read version Joy of PI have 10,000 digits. And if you want to remember the digits youself you could try lerning the Cadaeic Cadenza poem.
Since there are sieve equations for binary representations of pi, one could combine variables to store pieces of the value to increase precision. The only limitation to the precision on this method is conversion from binary to decimal, but even rational numbers can run into issues with that.
"In IEEE 754, the float data type, also known as single precision, is a 32-bit value that gives you a range of ±1.18×10−38 to ±3.4×1038 and about 7 digits of precision. That means that you can only accurately represent pi as 3.141592." "In this case pi (3.141592653589793), has been encoded into the double precision floating point number. Note that the true value of this double precision number is 3.14159265358979311599796346854. There are multiple ways to store a decimal number in binary with a varying level of precision."
© 2022 - 2025 — McMap. All rights reserved.