Calculating Pi |

package pi; import java.util.*; import java.math.BigInteger; import java.math.BigDecimal; /** * Calculates pi to an arbitrary number od decimal places * @author BotRejectsInc. */ public class Main { /** Creates a new instance of Main */ public Main() { } public static void main(String[] args) { int terms = 1; Scanner sc = new Scanner(System.in); while(true) { System.out.println("Enter a positive integer { 1, 2, 3, ...} or 0 to exit:"); terms = sc.nextInt(); if(terms < 1) { break; } BigDecimal pi = CalculatePi(terms); System.out.println(pi); //System.out.println("As an approximating double: " + pi.doubleValue()); } sc.close(); } private static BigDecimal CalculatePi(int terms) { //Need to use BidDecimals for this! int decimalPlaces = 1000; BigDecimal inverse_pi, Pi, numerator, divisor; BigDecimal[] sigma = new BigDecimal[terms]; BigDecimal[] term = new BigDecimal[terms]; //This coeeficient multiplies the final sum of terms BigDecimal root_2 = Sqrt_2(200); BigDecimal coefficient = root_2.multiply(BigDecimal.valueOf(2)).divide(BigDecimal.valueOf(9801), 1000, BigDecimal.ROUND_HALF_UP); //Calculate sum of terms for(int i = 0; i < terms; i++) { //Calculate next term try { numerator = new BigDecimal( LargeFactorial(4*i).multiply(BigInteger.valueOf(1103 + 26390*i)) ); divisor = new BigDecimal( LargeFactorial(i).pow(4).multiply( BigInteger.valueOf(396).pow(4*i) ) ); term[i] = ( numerator.divide(divisor, decimalPlaces, BigDecimal.ROUND_HALF_UP) ); } catch( IllegalArgumentException e ) { System.out.println(e.getMessage()); } //Summate terms and store cumulative sum in the array sigma if(i == 0) { sigma[i] = term[i]; } if(i > 0) { sigma[i] = term[i].add(sigma[i-1]); } } //Final algebra multiply sum by coefficient and invert inverse_pi = coefficient.multiply(sigma[terms-1]); BigDecimal unity = new BigDecimal("1"); Pi = unity.divide(inverse_pi, decimalPlaces, BigDecimal.ROUND_HALF_UP); return Pi; } //Calculates factorials of large values using BigInteger private static BigInteger LargeFactorial(int n) throws IllegalArgumentException { if(n == -1) { throw new IllegalArgumentException("Negative factorial not defined"); } BigInteger factorial = new BigInteger("1"); if(n == 0 || n == 1) { return factorial; } while(n > 1) { BigInteger N = BigInteger.valueOf(n); factorial = factorial.multiply(N); //System.out.println(factorial); n--; } return factorial; } private static BigDecimal Sqrt_2(int terms) { BigDecimal root_2; BigDecimal[] buffer = new BigDecimal[terms+1]; BigDecimal initial = new BigDecimal("1.4"); buffer[0] = initial; for(int i = 1; i <= terms; i++) { buffer[i] = buffer[i-1].divide(BigDecimal.valueOf(2), terms*5, BigDecimal.ROUND_HALF_UP).add(BigDecimal.valueOf(1).divide(buffer[i-1], terms*5, BigDecimal.ROUND_HALF_UP)); } root_2 = buffer[terms]; return root_2; } } |

Pi is an irrational number, meaning that it apparently has an infinite number of decimal places. My pocket

calculator gives pi as 3.141592654, a reasonable approximation! As someone who programs computers for a

hobby, I like to give myself little programming challenges and tasks to do. The aim of this program was to

calculate pi to at least 100 decimal places (d.p.) accuracy (essentially to an arbitrary accuracy, within the limits of

computation power on a desktop PC). (This kept me busy for a couple of afternoons and was very interesting

and educational!) The solution I obtained demonstrates the use of the Java BigInteger and BigDecimal classes,

the use of iteration and summation of terms in mathematical series. For this purpose I used the following

approximation formula for pi (the Ramanujan formula):

calculator gives pi as 3.141592654, a reasonable approximation! As someone who programs computers for a

hobby, I like to give myself little programming challenges and tasks to do. The aim of this program was to

calculate pi to at least 100 decimal places (d.p.) accuracy (essentially to an arbitrary accuracy, within the limits of

computation power on a desktop PC). (This kept me busy for a couple of afternoons and was very interesting

and educational!) The solution I obtained demonstrates the use of the Java BigInteger and BigDecimal classes,

the use of iteration and summation of terms in mathematical series. For this purpose I used the following

approximation formula for pi (the Ramanujan formula):

Note that this is a series approximation - we calculate a series of terms for k = 0, k = 1, k = 2, ..., etc. and

then add the terms together. The more decimal places of accuracy we require, the more terms we must

summate. This series is a good approximation for computation, since it converges very rapidly on the true

value of pi. We may wish to add tens or even hundreds of these terms together.

This looks simple enough, until you realise the problem caused by the factorials (! is factorial or 'shriek').

Recall that n! = n x n - 1, x, n-2, ..., x 2, so for example, 5! = 5 x 4 x 3 x 2 = 120 and 10! = 3 628 800 (0! is

set equal to 1 by definition). These numbers rapidly get very big! The highest my pocket calculator can

go is 69! = 1.711224524 E+98. For the 20th term, k = 20, we already need to compute 80!

It would be straightforward to multiply Java (or C#) integers (int) together to compute n! except that an int

is 32 bit and so can only hold a positive number as large as 2^(32)-1, that is 4294967295. A long is a 64

bit representation of whole numbers and so can go much higher, up to about 1.845 E+19, but is still not

large enough! Using these data types can result in overflow errors: if the number becomes to large for the

int or long container then only part of the number is stored and it may have a negative value. To avoid this

Java and C# come equipped with a BigInteger class which can store and manipulate whole numbers of

arbitrary length. Using this class, we can write a function to compute arbitrarily large factorials, such as:

then add the terms together. The more decimal places of accuracy we require, the more terms we must

summate. This series is a good approximation for computation, since it converges very rapidly on the true

value of pi. We may wish to add tens or even hundreds of these terms together.

This looks simple enough, until you realise the problem caused by the factorials (! is factorial or 'shriek').

Recall that n! = n x n - 1, x, n-2, ..., x 2, so for example, 5! = 5 x 4 x 3 x 2 = 120 and 10! = 3 628 800 (0! is

set equal to 1 by definition). These numbers rapidly get very big! The highest my pocket calculator can

go is 69! = 1.711224524 E+98. For the 20th term, k = 20, we already need to compute 80!

It would be straightforward to multiply Java (or C#) integers (int) together to compute n! except that an int

is 32 bit and so can only hold a positive number as large as 2^(32)-1, that is 4294967295. A long is a 64

bit representation of whole numbers and so can go much higher, up to about 1.845 E+19, but is still not

large enough! Using these data types can result in overflow errors: if the number becomes to large for the

int or long container then only part of the number is stored and it may have a negative value. To avoid this

Java and C# come equipped with a BigInteger class which can store and manipulate whole numbers of

arbitrary length. Using this class, we can write a function to compute arbitrarily large factorials, such as:

//Calculates factorials of large values using BigInteger private static BigInteger LargeFactorial(int n) throws IllegalArgumentException { if(n == -1) { throw new IllegalArgumentException("Negative factorial not defined"); } BigInteger factorial = new BigInteger("1"); if(n == 0 || n == 1) { return factorial; } while(n > 1) { BigInteger N = BigInteger.valueOf(n); factorial = factorial.multiply(N); //System.out.println(factorial); n--; } return factorial; } |

Generally speaking, the factorial of a negative number is not defined (though it can be in advanced

mathematics) and so we need to ensure that only zero or a positive integer is passed to the function.

There are a number of ways to do this, I chose to have it throw an IllegalArgumentException if it is passed

a negative argument.

Testing this function it certainly agrees with my calculator up to 69! and so presumably is accurate for

larger factorials. Here are a few test results:

mathematics) and so we need to ensure that only zero or a positive integer is passed to the function.

There are a number of ways to do this, I chose to have it throw an IllegalArgumentException if it is passed

a negative argument.

Testing this function it certainly agrees with my calculator up to 69! and so presumably is accurate for

larger factorials. Here are a few test results:

The factorial of 10 is: 3628800

The factorial of 12 is: 479001600

The factorial of 13 is: 6227020800

The factorial of 14 is: 87178291200

The factorial of 15 is: 1307674368000

The factorial of 16 is: 20922789888000

The factorial of 17 is: 355687428096000

The factorial of 18 is: 6402373705728000

The factorial of 20 is: 2432902008176640000

The factorial of 30 is: 265252859812191058636308480000000

The factorial of 40 is: 815915283247897734345611269596115894272000000000

The factorial of 50 is: 30414093201713378043612608166064768844377641568960512000000000000

The factorial of 69 is:

171122452428141311372468338881272839092270544893520369393648040923257279754140647424000000000000000

The factorial of 100 is:

93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

The factorial of 200 is:

78865786736479050355236321393218506229513597768717326329474253324435944996340334292030428401198462390417721213891963883025764279024263710506192662495282993111346285727076331723739698894392244562145166424025403329186413122742829485327752424240757390324032125740557956866022603190417032406235170085879617892222278962370389737472000000

0000000000000000000000000000000000000000000

The factorial of 12 is: 479001600

The factorial of 13 is: 6227020800

The factorial of 14 is: 87178291200

The factorial of 15 is: 1307674368000

The factorial of 16 is: 20922789888000

The factorial of 17 is: 355687428096000

The factorial of 18 is: 6402373705728000

The factorial of 20 is: 2432902008176640000

The factorial of 30 is: 265252859812191058636308480000000

The factorial of 40 is: 815915283247897734345611269596115894272000000000

The factorial of 50 is: 30414093201713378043612608166064768844377641568960512000000000000

The factorial of 69 is:

171122452428141311372468338881272839092270544893520369393648040923257279754140647424000000000000000

The factorial of 100 is:

93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

The factorial of 200 is:

78865786736479050355236321393218506229513597768717326329474253324435944996340334292030428401198462390417721213891963883025764279024263710506192662495282993111346285727076331723739698894392244562145166424025403329186413122742829485327752424240757390324032125740557956866022603190417032406235170085879617892222278962370389737472000000

0000000000000000000000000000000000000000000

So we can calculate the factorials we need. What about the square-root of 2 which appears in the

coefficient that multiplies the sum of terms? This is an irrational number, like pi, with an apparently infinite

number of d.p. Using standard elementary data types, such as doubles or floats, again is insufficient - the

values overflow. Using doubles introduces an error into the 15th d.p. so we can not use something like:

double a = (2*Math.sqrt(2)) / 9801;

Java has another class for fractions, the BigDecimal class, but this does not support a square-root

method! The solution I used was to approximate root 2 using the following iterative formula:

coefficient that multiplies the sum of terms? This is an irrational number, like pi, with an apparently infinite

number of d.p. Using standard elementary data types, such as doubles or floats, again is insufficient - the

values overflow. Using doubles introduces an error into the 15th d.p. so we can not use something like:

double a = (2*Math.sqrt(2)) / 9801;

Java has another class for fractions, the BigDecimal class, but this does not support a square-root

method! The solution I used was to approximate root 2 using the following iterative formula:

This is an iterative formula that needs an initial 'guess' to get going. The value of the initial guess is not

critical, the iteration converges to root 2, but using a closer value, like 1, will mean that it converges more

quickly. I used 1.4 as an initial value. This formula does indeed converge very rapidly. Again, it is an

approximation, so we need to make sure that it is at least as accurate as we need to estimate pi to our

required degree of accuracy. I wrote a function which takes the number of terms or iterations as an

argument:

critical, the iteration converges to root 2, but using a closer value, like 1, will mean that it converges more

quickly. I used 1.4 as an initial value. This formula does indeed converge very rapidly. Again, it is an

approximation, so we need to make sure that it is at least as accurate as we need to estimate pi to our

required degree of accuracy. I wrote a function which takes the number of terms or iterations as an

argument:

private static BigDecimal Sqrt_2(int terms) { BigDecimal root_2; BigDecimal[] buffer = new BigDecimal[terms+1]; BigDecimal initial = new BigDecimal("1.4"); buffer[0] = initial; for(int i = 1; i <= terms; i++) { buffer[i] = buffer[i-1].divide(BigDecimal.valueOf(2), terms*5, BigDecimal.ROUND_HALF_UP).add(BigDecimal.valueOf(1).divide(buffer[i-1], terms*5, BigDecimal.ROUND_HALF_UP)); } root_2 = buffer[terms]; return root_2; } |

The result of each iteration is stored as a BigDecimal in the array buffer[], and these are then summated.

This was done as the BigDecimal, like the BigInteger type, is immutable (though references can be

reassigned as in the LargeFactorial() function above which did not use an array to store intermediate

values). It also means we can inspect any of the terms in future should we wish to do so. Be careful when

carrying out division with the BigDecimal class to specify a limit to the number of decimal places of

accuracy, as the division function can not cope with an endless string of digits (again ensure that the limit

used is at least sufficient for our requirements, by setting it as required in this case). I am also following

standard procedure of rounding halfs up.

We can test the results against the official value of root 2:

1.41421356237309504880168872420969807856967187537694807317667973799 ...

I obtained:

1.4142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415728

with 100 iterations. This seems to be working fine, so lets test the final program, which is included below

as a single listing (you can run this as a single class and file console app in an IDE like NetBeans, hence

the use of static functions).

This was done as the BigDecimal, like the BigInteger type, is immutable (though references can be

reassigned as in the LargeFactorial() function above which did not use an array to store intermediate

values). It also means we can inspect any of the terms in future should we wish to do so. Be careful when

carrying out division with the BigDecimal class to specify a limit to the number of decimal places of

accuracy, as the division function can not cope with an endless string of digits (again ensure that the limit

used is at least sufficient for our requirements, by setting it as required in this case). I am also following

standard procedure of rounding halfs up.

We can test the results against the official value of root 2:

1.41421356237309504880168872420969807856967187537694807317667973799 ...

I obtained:

1.4142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415728

with 100 iterations. This seems to be working fine, so lets test the final program, which is included below

as a single listing (you can run this as a single class and file console app in an IDE like NetBeans, hence

the use of static functions).

The official value of pi to 100 d.p. is:

3.1415926535 8979323846 2643383279 5028841971 6939937510 5820974944 5923078164

0628620899 8628034825 3421170679 8214808651 3282306647 0938446095 5058223172 5359408128

4811174502 8410270193 8521105559 6446229489 5493038196 4428810975 6659334461 2847564823

3786783165 2712019091 4564856692 3460348610 4543266482 1339360726 0249141273 7245870066

0631558817 4881520920 9628292540 9171536436 7892590360 0113305305 4882046652 1384146951

9415116094 3305727036 5759591953 0921861173 8193261179 3105118548 0744623799 6274956735

1885752724 8912279381 8301194912 9833673362 4406566430 8602139494 6395224737 1907021798

6094370277 0539217176 2931767523 8467481846 7669405132 0005681271 4526356082 7785771342

7577896091 7363717872 1468440901 2249534301 4654958537 1050792279 6892589235 4201995611

2129021960 8640344181 5981362977 4771309960 5187072113 4999999

With just 100 terms of the Ramanujan formula I obtained:

100 terms:

3.1415926535 8979323846 2643383279 5028841971 6939937510 5820974944 5923078164

0628620899 8628034825 3421170679 8214808651 3282306647 0938446095 5058223172 5359408128

4811174502 8410270193 8521105559 6446229489 5493038196 4428810975 6659334461 2847564823

3786783165 2712019091 4564856692 3460348610 4543266482 1339360726 0249141273 7245870066

0631558817 4881520920 9628292540 9171536436 7892590360 0113305305 4882046652 1384146951

9415116094 3305727036 5759591953 0921861173 8193261179 3105118548 0744623799 6274956735

1885752724 8912279381 8301194912 9833673362 4406566430 8602139494 6395224737 1907021798

6094370277 0539217176 2931767523 8467481846 7669405132 0005681271 4526356082 7785771342

7577896091 7363717872 1468440901 2249534301 4654958537 1050792279 6892589235 4201995611

2129021960 8640344181 5981362977 4771309960 5187072113 4999999

(8372978049 9510597317 3281609631 9125322977 4937741372 6512882026 8619471299

1299221042 1681419479 8705721777 9038338672 9847360285 5172958903 9759373423 2587356335

2043217855 3912536912 7618906019 4054262794 9728492904 0581146064 0009566664 5502282746

007 )

Accurate to 100 d.p. plus a few, and the calculation took only a fraction of a second!

With fewer terms less accuracy is obtained:

20 terms:

3.1415926535 8979323846 2643383279 5028841971 6939937510 5820974944 5923078164

0628620899 8628034825 3421170679 8214808651 3282306647 0938446095 5058223172 5359408128

481117450 (rest inaccurate)

30 terms:

3.1415926535 8979323846 2643383279 5028841971 6939937510 5820974944 5923078164

0628620899 8628034825 3421170679 8214808651 3282306647 0938446095 5058223172 5359408128

4811174502 8410270193 8521105559 6446229489 5493038196 4428810975 6659334461 2847564823

37867831 (rest inaccurate).

*Developing the Program Further*

The program could be modified by enabling the user to obtain pi to a requested accuracy: the user could

input the number of decimal places required. The solution here would involves looping indefinitely,

summating the terms in the series and checking the answer each time (with n terms) with the previous

answer (with n - 1 terms) and seeing which digits remain unchanged. If we required 20 d.p. of accuracy

then the solution is obtained once addition of successive terms causes no further change in the first 21

d.p. (21 to avoid rounding errors - it is prudent to obtain an extra digit of accuracy than required to avoid

rounding errors). This works because each successive term in the series is smaller than the proceeding

term - adding additional terms after the first few changes the value only slightly. The computed value

converges or settles down, to a value closer to pi with each term added.

Of course, if one wanted to beat the current world record by computing pi to trillions of decimal places,

then speed becomes a real issue. Such calculations are performed on supercomputers, and one would

probably use lower-level programming, avoiding the complexities (and niceties) of object-oriented

programming (OOP) which I have used, by using C++ or C, for extra speed (at the possible expense of

coding convenience). The last time I checked, the current world record was 10 trillion digits, calculated by

the Japanese mathematician Shigeru Kondo in 2011.

A very different approach to estimating pi, using probability theory, is illustrated in the article on Monte

Carlo methods.

3.1415926535 8979323846 2643383279 5028841971 6939937510 5820974944 5923078164

0628620899 8628034825 3421170679 8214808651 3282306647 0938446095 5058223172 5359408128

4811174502 8410270193 8521105559 6446229489 5493038196 4428810975 6659334461 2847564823

3786783165 2712019091 4564856692 3460348610 4543266482 1339360726 0249141273 7245870066

0631558817 4881520920 9628292540 9171536436 7892590360 0113305305 4882046652 1384146951

9415116094 3305727036 5759591953 0921861173 8193261179 3105118548 0744623799 6274956735

1885752724 8912279381 8301194912 9833673362 4406566430 8602139494 6395224737 1907021798

6094370277 0539217176 2931767523 8467481846 7669405132 0005681271 4526356082 7785771342

7577896091 7363717872 1468440901 2249534301 4654958537 1050792279 6892589235 4201995611

2129021960 8640344181 5981362977 4771309960 5187072113 4999999

With just 100 terms of the Ramanujan formula I obtained:

100 terms:

3.1415926535 8979323846 2643383279 5028841971 6939937510 5820974944 5923078164

0628620899 8628034825 3421170679 8214808651 3282306647 0938446095 5058223172 5359408128

4811174502 8410270193 8521105559 6446229489 5493038196 4428810975 6659334461 2847564823

3786783165 2712019091 4564856692 3460348610 4543266482 1339360726 0249141273 7245870066

0631558817 4881520920 9628292540 9171536436 7892590360 0113305305 4882046652 1384146951

9415116094 3305727036 5759591953 0921861173 8193261179 3105118548 0744623799 6274956735

1885752724 8912279381 8301194912 9833673362 4406566430 8602139494 6395224737 1907021798

6094370277 0539217176 2931767523 8467481846 7669405132 0005681271 4526356082 7785771342

7577896091 7363717872 1468440901 2249534301 4654958537 1050792279 6892589235 4201995611

2129021960 8640344181 5981362977 4771309960 5187072113 4999999

(8372978049 9510597317 3281609631 9125322977 4937741372 6512882026 8619471299

1299221042 1681419479 8705721777 9038338672 9847360285 5172958903 9759373423 2587356335

2043217855 3912536912 7618906019 4054262794 9728492904 0581146064 0009566664 5502282746

007 )

Accurate to 100 d.p. plus a few, and the calculation took only a fraction of a second!

With fewer terms less accuracy is obtained:

20 terms:

3.1415926535 8979323846 2643383279 5028841971 6939937510 5820974944 5923078164

0628620899 8628034825 3421170679 8214808651 3282306647 0938446095 5058223172 5359408128

481117450 (rest inaccurate)

30 terms:

3.1415926535 8979323846 2643383279 5028841971 6939937510 5820974944 5923078164

0628620899 8628034825 3421170679 8214808651 3282306647 0938446095 5058223172 5359408128

4811174502 8410270193 8521105559 6446229489 5493038196 4428810975 6659334461 2847564823

37867831 (rest inaccurate).

input the number of decimal places required. The solution here would involves looping indefinitely,

summating the terms in the series and checking the answer each time (with n terms) with the previous

answer (with n - 1 terms) and seeing which digits remain unchanged. If we required 20 d.p. of accuracy

then the solution is obtained once addition of successive terms causes no further change in the first 21

d.p. (21 to avoid rounding errors - it is prudent to obtain an extra digit of accuracy than required to avoid

rounding errors). This works because each successive term in the series is smaller than the proceeding

term - adding additional terms after the first few changes the value only slightly. The computed value

converges or settles down, to a value closer to pi with each term added.

Of course, if one wanted to beat the current world record by computing pi to trillions of decimal places,

then speed becomes a real issue. Such calculations are performed on supercomputers, and one would

probably use lower-level programming, avoiding the complexities (and niceties) of object-oriented

programming (OOP) which I have used, by using C++ or C, for extra speed (at the possible expense of

coding convenience). The last time I checked, the current world record was 10 trillion digits, calculated by

the Japanese mathematician Shigeru Kondo in 2011.

A very different approach to estimating pi, using probability theory, is illustrated in the article on Monte

Carlo methods.