7

EDIT: The requirement was vague and instead of calculating the n-th digit of pi they just wanted pi to the n-th digit not going beyond floats limitation so the brute force way worked for the requirements.

I need to calculate PI the the n-th digit and I wanted to try using the BBP formula but am having difficulties. The equation I typed up doesn't seem to be giving me PI correctly.

(1 / pow(16,n))((4 / (8 * n + 1)) - (2 / (8 * n + 4)) - (1 / (8 * n + 5)) - (1 / (8 * n + 6)))

I was successful with the brute force way of finding PI but that is only so accurate and finding the nth number is difficult.

(4 - (4/3) + (4/5) - (4/7)...)

I wanted to find out if anyone had a better idea of how to do this or maybe help with my BBP equation on what I messed up?

Thank you,
LF4

Functional but not very accurate until a few iterations in and then you have to disreguard the last few.

#include <iostream>

using namespace std;

int main()
{
    int loop_num = 0;
    cout << "How many digits of pi do you want?: ";
    cin  >> loop_num;

    double my_pi = 4.0;
    bool add_check = false;
    int den = 3;
    for (int i = 0; i < loop_num; i++)
    {
        if (add_check)
        {
            my_pi += (4.0/den);
            add_check = false;
            den += 2;
        }
        else
        {
            my_pi -= (4.0/den);
            add_check = true;
            den += 2;
        }
    }
    cout << "Calculated PI is: " << my_pi << endl;
    system("pause");

    return 0;
}

What I'm hoping would be a better program.

#include <iostream>
#include <cmath>

using namespace std;

const double PI_BASE = 16.0;

int main()
{
    int loop_num = 0;
    cout << "How many digits of pi do you want?: ";
    cin  >> loop_num;

    double my_pi = 0.0;
    for (int i = 0; i <= loop_num; i++)
    {
        my_pi += ( 1.0 / pow(PI_BASE,i) )( (4.0 / (8.0 * i + 1.0)) -
                                           (2.0 / (8.0 * i + 4.0)) -
                                           (1.0 / (8.0 * i + 5.0)) -
                                           (1.0 / (8.0 * i + 6.0)) );
    }
    cout << "Calculated PI is: " << my_pi << endl;
    system("pause");

    return 0;
}
krizzo
  • 1,823
  • 5
  • 30
  • 52
  • How much precision do you expect? And how does that compare to the precision supported by the type you are using? What about the numeric properties of the algorithm...minus signs always mean having to worry about loss of precision. – dmckee --- ex-moderator kitten Sep 01 '11 at 03:03
  • I wanted to calculate PI as we know it's either correct or not(excluding the last digit which might be rounded). The program prompts the user for how many significant digits of pi they want then calculates it. From my understanding the BBP formula would sum up for each number 0 to infinity. Each time would be one more digit of pi. I'll add my code to help and understanding what I want. – krizzo Sep 01 '11 at 04:48
  • 2
    The built in floating point representations will support only 6-7 (32 bit) or 15-16 (64 bit) (and possibly 17-18 (80 bit)) decimal digits of precisions. To get more than that you will have to use a arbitrary precision package of some kind. There is a document floating around the internet called *What Every Computer Scientist Should Know About Floating-Point Arithmetic*. You need to read it. – dmckee --- ex-moderator kitten Sep 01 '11 at 05:00
  • True I remember a C++ class last year that I took where the teacher taught us about floats and math how we had to be careful with them. He also said finance programs don't use floats only ints because of the rounding errors. – krizzo Sep 01 '11 at 05:05
  • 1
    Are you trying compute ALL the digits from the first to the Nth? Or are you just trying to get a few digits around the Nth binary digit? There is no reason to use the BBP formula if you want all the digits as there are MUCH faster formulas out there for Pi. If you want all the digits from the 1st to the Nth, you will need arbitrary precision arithmetic to do it. – Mysticial Sep 01 '11 at 05:11
  • All digits; user inputs 4, output 3.141; input 6, output 3.14159 if that makes sense. Maybe I understood the BBP formula wrong it only calculates the nth digit not to the nth digit? – krizzo Sep 01 '11 at 05:16
  • 1
    The BBP formula can be used to compute all the digits, but there are much faster formulas do that. (see the Chudnovsky formula: http://en.wikipedia.org/wiki/Chudnovsky_algorithm) - The power of the BBP formaula is that it allows you to directly compute the Nth binary digit of Pi without computing the digits before it. – Mysticial Sep 01 '11 at 05:19
  • Ahh that makes sense now. I thought for some reason "numbers before it" was like the Fibonacci where it's based on the numbers before. Thank you I'll look in to the Chudnovsky formula. – krizzo Sep 01 '11 at 05:21

3 Answers3

4

The BBP formula is not suitable for easy finding of n-th decimal digit as it easily returns hex and only hex digits. So to recalculate into decimals you will need to collect all hex digits.

It is much better to use Newton formula:

Pi/2 = 1 + 1/3 + 1*2/3*5 + 1*2*3/3*5*7 + .... n!/(2n+1)!! + ....

It collapses to Horner's scheme:

Pi/2 = 1 + 1/3*(1 + 2/5*(1 + 3/7*(1 + ...... n/(2n+1)*(1) ..... )))

So you have Pi written as a positional series where on each fractional position you have different base used (n/(2n+1)), and all digits are equal to 2. It obviously converges, as that base is less than 1/2, so to calculate Pi up to n significant decimal dgits you need no more than log_2(10)*n terms (N = 10*n/3+1 is perfect stuff).

You start with the array of N integer elements, all equals 2, and repeatedly, n times, do the following:

1.) Multiply all elements by 10.

2.) Recalculate each element[k] (from N down to 1) to have a "digit" less then denominator (2*k+1),
but at the same time you need to move a qoutient to the left position, so:
q = element[k] / (2*k+1);
element[k] %= (2*k+1);
element[k-1] += q * k; //k is the counter, so don't forgrt to multiply.

3.) take element[0]. It equals 10 * first digit, so you need to output element[0] / 10 and store
element[0] %= 10;

BUT there is a clue: the maximal sum for maximal possible digits (2*n) of Newton formula is 2. So you can obtain as much as 19/10 from element[1]. When adding to element[0] (multiplied by 10 in step 1.) you can obtain 90+19=109. So it sometimes happens outputted digit will be [10]. In such a case you know, that the correct digit is 0, and the 1 must be added to the previously outputted digit.

There are two ways of solving this issue:

1.) Not to output the last digit until the next one is calculated. Moreover, store the number of consecutive nines and output them as nines or as 1 followed by zeros depending on first non 9 digit.

2.) Put outputted digits into result array, so you can easily add 1 if [10] occurs.

On my PC I can calculate (in Java) 10,000 decimal digits in 10 s. The complexity is O(n^2).

The values of element[k] never exceeds 12*k, so using 64-bit long type on fast machine you can calculate more than 10^15 digits (very robust approx.).

Jav_Rock
  • 22,059
  • 20
  • 123
  • 164
Tomek
  • 41
  • 1
  • BBP isn't just for hex digits; BBP itself yields binary digits. This can be trivially mapped to hex digits because 16 is a power of 2, whereas 10 is not. You could also generate, say, the Nth octal digit of pi using BBP. Calculating the nth hexadecimal digit is simply calculating the [n, n+3] binary digits together. – Daniel Papasian Jan 24 '14 at 18:53
  • @DanielPapasian: Sure, but try to switch to decimal, and you'll see where the problem is. Then the bits in an arbitrary place in the bit stream won't help you much without knowing what was the bits before them, and what is their influence to the decimal digits you need to know. (You know, carries from lower positions & stuff like that.) – SasQ Jan 27 '15 at 12:20
4

Regardless of what formula you use, you will need arbitrary precision arithmetic to get more than 16 digits. (Since "double" only has 16 digits of precision).

The Chudnovsky Formula is the fastest known formula for computing Pi and converges at 14 digits per term. However, it is extremely difficult to implement efficiently.

Due to the complexity of this formula, there's no point in using to compute Pi to less than a few thousand digits. So don't use it unless you're ready to go all-out with arbitrary precision arithmetic.

A good open-sourced implementation of the Chudnovsky Formula using the GMP library is here: http://gmplib.org/pi-with-gmp.html

Mysticial
  • 464,885
  • 45
  • 335
  • 332
  • 4
    The original BBP paper states explicitly that their method benefits from not needing arbitrary precision arithmetic. How do you justify what you said here in that light? – JeremyKun Jan 05 '14 at 20:22
  • @JeremyKun The trouble with the BBP extraction algorithm is that the numerical error builds up `log(n)` with the number of digits. So if you're using double precision with 53 bits of precision, you won't be able to sum up more than 2^53 terms before you've already lost all of it to round-off error. – Mysticial Jan 05 '14 at 20:40
  • The round-off error + the # of digits you wish to extract must be smaller than 53 bits. So yes, if you're looking for a small number of digits that are not too far behind the decimal place, it's possible to avoid all arbitrary precision arithmetic. But you won't be able to extract say 100 digits at some arbitrary offset. If you want to use the BBP formula to extract more than a few digits at a time, you will need to do arithmetic wider than the word-size. At the very least, you will need to be able to divide two word-size integers and save a result that is longer than a word. – Mysticial Jan 05 '14 at 20:41
  • In all cases, you avoid the need to perform multiplication of large integers - which is considered the more difficult part of arbitrary precision arithmetic. I'm pretty sure this is what the paper is referring to. – Mysticial Jan 05 '14 at 20:42
  • The fact that they call it "efficient" when it depends on d = 2^log(d) is already quite misleading... As computer scientists they should know better. – JeremyKun Jan 06 '14 at 01:33
  • @JeremyKun The BBP formula for computing a small # of digits at offset N is quasi-linear in N. Computing all the digits to N is also quasi-linear. The difference is that the BBP formula requires `O(1)` memory and is embarrassingly parallel. That's why it is "efficient". – Mysticial Jan 06 '14 at 01:55
  • Typically problems which take just a number N as input (such as primality, factoring, etc) are evaluated in terms of log(N), the length of the input string. This make BBP an exponential algorithm. In particular, BBP doesn't rule out the use of the digits of pi as a cryptographically secure pseudo-random number generator. – JeremyKun Jan 06 '14 at 14:50
4

It looks like you are trying to calculate the decimal digits of π when the BBP formula is mainly used to calculate arbitrary hexadecimal digits of π. Basically, the BBP formula can be used to calculate the nth hexadecimal digit of π without computing the previous digits, hex digits 0, 1, ..., n - 1.

David H. Bailey (the Bailey of Bailey–Borwein–Plouffe) has written C and Fortran code to calculate the nth hexadecimal digit of π using the BBP formula. On a machine with IEEE 754 double arithmetic, it is accurate up to n ≈ 1.18 × 107 counting from 0; i.e. π = (3.243F6A8...)16 so the output of the program when n = 3 begins with “F”:

 position = 3
 fraction = 0.963509103793105
 hex digits =  F6A8885A30

I like to modify the C version slightly so that n (named id in the code) can be overridden by a command line argument:

--- piqpr8.c.orig   2011-10-08 14:54:46.840423000 -0400
+++ piqpr8.c    2011-10-08 15:04:41.524437000 -0400
@@ -14,14 +14,18 @@
 /*  David H. Bailey     2006-09-08 */

 #include <stdio.h>
+#include <stdlib.h>
 #include <math.h>

-main()
+int main(int argc, char *argv[])
 {
   double pid, s1, s2, s3, s4;
   double series (int m, int n);
   void ihex (double x, int m, char c[]);
   int id = 1000000;
+  if (argc == 2) {
+    id = atoi(argv[1]);
+  }
 #define NHX 16
   char chx[NHX];

@@ -36,6 +40,8 @@
   ihex (pid, NHX, chx);
   printf (" position = %i\n fraction = %.15f \n hex digits =  %10.10s\n",
   id, pid, chx);
+
+  return EXIT_SUCCESS;
 }

 void ihex (double x, int nhx, char chx[])
Daniel Trebbien
  • 38,421
  • 18
  • 121
  • 193