Calculate Pi in Java!

by 3DPrintingEnthusiast in Circuits > Computers

297 Views, 1 Favorites, 0 Comments

Calculate Pi in Java!

Presentation1_page-0001.jpg

Pi, a well known number equaling 3.1415926... But how can we actually calculate Pi to such incredible accuracy (105 trillion digits in 2024)? Are they actually measuring pieces of string around circles? No, of course not. They use clever algorithms to get good approximations of Pi. The following methods are examples of such algorithms.

Method 1 : Difficulty: easy

Number of Digits calculated in a day: 11

Method 2 : Difficulty: medium

Number of Digits calculated in a day: 6770

Supplies

Java.png
  • Java installed on a computer

The Basic Principle

PiInfiniteSeries.png

This method for calculating pi relies on the fact that π/4 = arctan(1). So all we have to do is calculate the arctan of 1 and multiply it by 4 to get π. The arctan can be approximated with an infinite series. An infinite series is a long repeating pattern of math symbols that usually converges to a number. For example : 1 + 1/2 + 1/4 + 1/8 + 1/16 + 1/32 + ... = 2.

To compute an arctan you can use this infinite series : arctan(n) = n^1/1 - n^3 / 3 + n^5 / 5 - n^7 / 7 + n^9 / 9... The signs alternate from (-) to (+) to (-) and the other numbers are all odd numbers starting from 1.

If we substitute 1 for n we get this π/4 = 1/1 - 1/3 + 1/5 - 1/7 + 1/9 ...

Now if we multiply both sides by 4 we get π = 4/1 - 4/3 + 4/5 - 4/7 + 4/9...

Method 1 : Creating the Variables

Screenshot 2024-04-11 at 8.41.55 PM.png
  • Create a new Java project with the Main Method
  • Create a variable of type double to store the decimal value of pi and call it piCalculation. Set it equal to 0.
  • Create another double and call it oddNumber. Set it equal to 1 This will store the denominator in the infinite series.
public class Main {

public static void main(String[] args) {
double piCalculation = 0;
double oddNumber = 1;
}
}

Method 1 : Calculation

Screenshot 2024-04-11 at 8.43.21 PM.png
  • Add a while loop and set its condition to be 1==1
  • Update the value of piCalculation to be piCalculation + 4/oddNumber
  • Change the value of oddNumber by 2 (gets you the next odd number)
  • Update the value of piCalculation again to be piCalculation - 4/oddNumber (- NOT +)
  • Add 2 to oddNumber
piCalculation = piCalculation + 4/oddNumber;
oddNumber = oddNumber+2;
piCalculation = piCalculation - 4/oddNumber;
oddNumber = oddNumber+2;

However, the longer you let it run the less digits of pi you get for the next 100 rounds.

Method 1 : Display

Screenshot 2024-04-11 at 9.15.48 PM.png
Screenshot 2024-04-11 at 9.14.54 PM.png

We want to see our progress so we can add :

System.out.println("π ≈ " + piCalculation);

To make the update on the current value occur less frequently, we can encapsulate the last step in a for-loop and set the loop time to 100 times :

for (int i = 0; i < 100; i++) {
piCalculation = piCalculation + 4 / oddNumber;
oddNumber = oddNumber + 2;
piCalculation = piCalculation - 4 / oddNumber;
oddNumber = oddNumber + 2;
}

However, using the Leibniz Method (the formula we are using) you get less and less digits of pi for 100 rounds the more digits you calculated. To compensate for this you can add a variable of type long and call it roundsBeforeCurrentPrint. Set it equal to 10. In your for-loop change the 100 to the new variable roundsBeforeCurrentPrint. Now every time you print the value of pi, add 10 to roundsBeforeCurrentPrint :

long roundsBeforeCurrentprint = 10;
while (1==1){
for (int i = 0; i < roundsBeforeCurrentprint; i++) {
piCalculation = piCalculation + 4 / oddNumber;
oddNumber = oddNumber + 2;
piCalculation = piCalculation - 4 / oddNumber;
oddNumber = oddNumber + 2;
}
System.out.println("π ≈ " + piCalculation);
roundsBeforeCurrentprint = roundsBeforeCurrentprint + 10;

Method 1 : Run the Program

Now just press run and watch the value get closer and closer to pi.

William Shanks used a similar method in 1853 and calculated over several years 607 digits, however his 527 digit and the ones that followed were all wrong.

This program is very simple but the only caveat is that the number converges very slowly.

Modern algorithms like the Chudnovsky Algorithm are way more complex but can add and average of 14 new digits per next iteration. The Chudnovsky Algorithm was used in 2024 to calculate more than 100 million digits of pi.

Method 2

If that was too easy for you, there are more complex faster converging versions of the first method using arctans (called Machin-Like Formulas). Now we are going to use the algorithm that William Shanks used in 1853 to calculate over 500 digits of pi. His formula goes like this : π/4 = 4 x arctan(1/5) - arctan(1/239).

Method 2 : Making an Arctan Method

Because we will use the arctan more than once in this version we are going to create a method called getArctan.

It is going to receive a double called arctanNumber and also return a double.

public static double getArctan(double arctanNumber) {

}

Create another two double variables and call them result (set result to 0) and oddNumber (set oddNumber to 1)

double result = 0;
double oddNumber = 1;

Add a for loop and set the runtime to be 20 steps

for (int i = 0; i < 20; i++) {

}

The Taylor series used to estimate an arctan is : arctan(n) = n^1/1 - n^3 / 3 + n^5 / 5 - n^7 / 7 + n^9 / 9...

We can split it up into repeating chunks : [+ n^1/1 - n^3 / 3] [+ n^5 / 5 - n^7 / 7] [+ n^9 / 9 - n^11 / 11]

Substitute n for arctanNumber and the numbers for the variable oddNumber and change oddNumber by 2 every half a chunk :

We get this repeating chunk :

[+ arctanNumber^oddNumber/oddNumber (add two to oddNumber) - arctanNumber^oddNumber/oddNumber (add two to odd number)]

Now we implement it in code :

for (double i = 0; i < 2000; i++) {
result = result + java.lang.Math.pow(arctanNumber,oddNumber) / oddNumber;
oddNumber = oddNumber + 2;
result = result - java.lang.Math.pow(arctanNumber,oddNumber) / oddNumber;
oddNumber = oddNumber + 2;
}

Method 2 : Using the Formula

Now in the main method we are going to implement the formula.

Set a double called pi to be 4 * (4 * arctan(1/5) - arctan(1/239))

Then print pi.

public static void main(String[] args) {
double pi = 4*(4*getArctan(1/5) - getArctan(1/239));
System.out.println(pi);
}

Method 2 : Running the Program

Press run and soon you will see pi to much greater accuracy than with the first method. Actually this method is so good that it maxes out the decimal places that can be used in a double. The solution : BigDecimal. BigDecimal is similar to BigInteger and, as the name implies, can store very large decimal numbers. You have to import it :

import java.math.BigDecimal;

You can create BigDecimals like this

BigDecimal example = new BigDecimal("0");

When you want to do calculations you have to use .add / .subtract / .multiply / .divide / .pow

BigDecimal a = new BigDecimal("5");
BigDecimal b = new BigDecimal("17");
BigDecimal c = new BigDecimal("0");
BigDecimal c = a.add(b)

BigBecimal calculates division-problems exact. This means if you do:

BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("3");
BigDecimal c = new BigDecimal("0");
BigDecimal c = a.divide(b)

The program tries to calculate 10/3 but it is an infinite decimal so it requires an infinite amount of storage and java throws an error. To solve this you have to use rounding mode. You also have to import it.

import java.math.RoundingMode;

To use it just add extra parameters to your divide function. The first parameter says how many digits of precision you want and the second parameter says if you want to round up or down. You usually use RoundingMode.HALF_UP because it rounds the numbers 1,2,3,4 down and the numbers 5,6,7,8,9 up, just like regular rounding in math.

BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("3");
BigDecimal c = new BigDecimal("0");
BigDecimal c = a.divide(b, 8000, RoundingMode.HALF_UP)

Method 2 : Replacing All the Doubles With BigDecimals

Next replace all the doubles with BigDecimals. It sometimes is very helpful if you break the calculations into smaller chunks for large calculation with bigDecimals. Make sure to use RoundingMode when dividing BigDecimals.

You can also upgrade your formula for calculating pi. I used a formula used in the 2002 world record pi calculation. Using a 64 node supercomputer Yasumasa Kanada was able to calculate 1,241,100,000,000 digits of pi with the following formula : π = 4 * ( 12*arctan(1/49) + 32*arctan(1/57) + 5*arctan(1/239) + 12*arctan(1/110443) )

import java.math.BigDecimal;
import java.math.RoundingMode;

public class Main {

public static void main(String[] args) {
BigDecimal pi;
BigDecimal twothreenine;
twothreenine = (BigDecimal.valueOf(1).divide(BigDecimal.valueOf(239)));
BigDecimal fournine;
fournine = (BigDecimal.valueOf(1).divide(BigDecimal.valueOf(49), 8000, RoundingMode.HALF_UP));
BigDecimal fiveseven;
fiveseven = (BigDecimal.valueOf(1).divide(BigDecimal.valueOf(57), 8000, RoundingMode.HALF_UP));
BigDecimal oneonezerofourfourthree;
oneonezerofourfourthree = (BigDecimal.valueOf(1).divide(BigDecimal.valueOf(110443), 8000, RoundingMode.HALF_UP));


twothreenine = getBigArctan(twothreenine);
fournine = getBigArctan(fournine);
fiveseven = getBigArctan(fiveseven);
oneonezerofourfourthree = getBigArctan(oneonezerofourfourthree);

twothreenine = twothreenine.multiply(BigDecimal.valueOf(-5));
fournine = fournine.multiply(BigDecimal.valueOf(12));
fiveseven = fiveseven.multiply(BigDecimal.valueOf(32));
oneonezerofourfourthree = oneonezerofourfourthree.multiply(BigDecimal.valueOf(12));

pi = ((((twothreenine.add(fournine)).add(fiveseven)).add(oneonezerofourfourthree)));

System.out.println(pi.multiply(BigDecimal.valueOf(4)));

}

public static BigDecimal getBigArctan(BigDecimal arctanNumber) {
BigDecimal result = new BigDecimal("0");
int oddNumber = 1;
BigDecimal intermediate;
BigDecimal intermediateresult;

intermediate = arctanNumber.pow(oddNumber);
interresult = intermediate;
intermediate = intermediate.divide(BigDecimal.valueOf(oddNumber), 8000, RoundingMode.HALF_UP);
result = result.add(intermediate);
oddNumber = oddNumber + 2;

interresult = (interresult.multiply(arctanNumber)).multiply(arctanNumber);
intermediate = interresult.divide(BigDecimal.valueOf(oddNumber), 8000, RoundingMode.HALF_UP);
result = result.subtract(intermediate);
oddNumber = oddNumber + 2;

for (double i = 1; i < 100; i++) {

intermediate = arctanNumber.pow(oddNumber);
interresult = intermediate;
intermediate = intermediate.divide(BigDecimal.valueOf(oddNumber), 8000, RoundingMode.HALF_UP);
result = result.add(intermediate);
oddNumber = oddNumber + 2;


interresult = (interresult.multiply(arctanNumber)).multiply(arctanNumber);
intermediate = interresult.divide(BigDecimal.valueOf(oddNumber), 8000, RoundingMode.HALF_UP);
result = result.subtract(intermediate);
oddNumber = oddNumber + 2;


System.out.println(i);
}
return result; }
}

Method 2 : Determining How Long You Want It to Run For

The algorithm calculates one arctan after the other and once it's finished with one arctan, it won't adjust the values anymore. This means that getting the correct value for i in the for loop in the getArctan function is crucial. A bigger value of i will result in more accuracy but will take longer to run. I ran the program with i = 1000 and got over 6700 digits (around 6-7 more digits per extra iteration in the for-loop). This however took a day to run. If you happen to have a supercomputer with a lot of data-storage, you can set i to 1.000.000 and go for it. However, I recommend testing the code with around 100 iterations in the for-loop and seeing how long it takes, then going from there. I also found it helpful to print the first 1000 digits of pi on your terminal after it has finished computing pi, so you can easily check if the digits are correct afterwards.

Method 2 Run the Program

Run the program and after a few hours of waiting, you'll have pi to several hundred digits of accuracy!