Calculate Pi

It is a beautiful day in the Twin Cities of Minneapolis and St. Paul. Currently it is sunny and the forecast call for highs in the lower 60s. My wife and I are planning on walking 5 miles after work.

The topic for this post is computing Pi. Unless you are working on some specific mathematical issue that requires an extreme precision, you would not attempt to generate Pi or care about how it is generated.  You would use the value (typically a constant) returned by the mathematical library in your favorite programming language.

I am watching a video and writing and experimenting with the code in order to fully understand the concepts being presented. In this case, the author of the video decided to use as an example a function that generates the value of Pi. I have not generated Pi since the time I was taking calculus. I like most (not to say all) the software engineers will use the value for the constant returned by the programming language in use (e.g., in Java I would use Math.PI) or if needed use a representation with higher precision than double and look up a Pi value with enough precision.

The first 100 digits for Pi:

3.1415926535 8979323846 2643383279 5028841971 6939937510 5820974944 5923078164 0628620899 8628034825 3421170679 ...

You can find additional precision for Pi here.

I searched on the web and found different approaches to generate Pi with increasing precision. Some of the approaches rely on the value of the constant Pi within while others use an infinite series which in theory can produce the precision you may be looking for. Note that you might have to use a library and language that represent floating point numbers with higher precision than double.

Let’s first take a look at the console output on the Eclipse IDE running the Java program I wrote:

loops: 1
computeGLSeries pi: 2.666666666666667
 computeNSeries pi: 3.1666666666666665
     usingLimit pi: 1.2246467991473532E-16
    x: 0.9717778563499451
   usingRandNum pi: 3.1415926535897936
loops: 10
computeGLSeries pi: 3.232315809405594
 computeNSeries pi: 3.1414067184965018
     usingLimit pi: 3.090169943749474
    x: 0.6275229454040527
   usingRandNum pi: 3.141592653589793
loops: 100
computeGLSeries pi: 3.1514934010709914
 computeNSeries pi: 3.1415924109719824
     usingLimit pi: 3.141075907812829
    x: 0.43229353427886963
   usingRandNum pi: 3.141592653589793
loops: 1000
computeGLSeries pi: 3.1425916543395442
 computeNSeries pi: 3.141592653340544
     usingLimit pi: 3.1415874858795636
    x: 0.01605445146560669
   usingRandNum pi: 3.1415926535897865
loops: 10000
computeGLSeries pi: 3.1416926435905346
 computeNSeries pi: 3.141592653589538
     usingLimit pi: 3.141592601912665
    x: 0.1688799262046814
   usingRandNum pi: 3.141592653589794
loops: 100000
computeGLSeries pi: 3.1416026534897203
 computeNSeries pi: 3.1415926535897865
     usingLimit pi: 3.1415926530730216
    x: 0.6796291470527649
   usingRandNum pi: 3.141592653589793
loops: 1000000
computeGLSeries pi: 3.1415936535887745
 computeNSeries pi: 3.141592653589787
     usingLimit pi: 3.1415926535846257
    x: 0.0207328200340271
   usingRandNum pi: 3.1415926535897905
loops: 10000000
computeGLSeries pi: 3.1415927535897814
 computeNSeries pi: 3.141592653589787
     usingLimit pi: 3.141592653589741
    x: 0.6565876007080078
   usingRandNum pi: 3.1415926535897936

A set of 4 methods is called with a parameter in order to compute the value of Pi. The first two methods iterate over a number of loops. The last two methods seems to use a formula which depends on the stored value of Pi.

The test code follows:

	// **** test scaffolding entry point ****
	public static void main(String[] args) {
		
		// **** ****
		final int MAX_LOOPS = 10000000;

		// **** instantiate object ****
		Pi pi = new Pi();
		
		// **** ****
		for (int loops = 1; loops <= MAX_LOOPS; loops *= 10) {
			
			// **** ****
			System.out.println("loops: " + loops);
			
			// **** compute Pi ****
			double val = pi.computeGLSeries(loops);
			
			// **** display pi ****
			System.out.println("computeGLSeries pi: " + val);
			
			// **** compute Pi ****
			val = pi.computeNSeries(loops);
			
			// **** display pi ****
			System.out.println(" computeNSeries pi: " + val);

			// **** compute Pi ****
			val = pi.usingLimit(loops);
			
			// **** display pi ****
			System.out.println("     usingLimit pi: " + val);
			
			// **** random number between 0 and 1 ****
			Random rand = new Random();
			double x = rand.nextDouble();
			while ((x <= 0.0) && (x >= 1.0))
				x = rand.nextDouble();
			
			// **** compute Pi ****
			val = pi.usingRandNum(x);
			
			// **** display pi ****
			System.out.println("   usingRandNum pi: " + val);
		}
		
	}

The code instantiates the class and repeatedly makes call to the four methods used to calculate Pi. In the first two cases the value of loops is used to iterate generating elements in a series which is added or subtracted form the final value. The two last methods use a function which seems to depend on the value of Pi stored in the system.

Let’s look at the first method in the Pi.java class:

	// **** Gregory-Leibniz infinite series ****
	public double computeGLSeries(int loops) {
		
		// **** ****
		double 	pi 	= 0.0;
		
		// **** loop computing Pi ****
		for (int i = 0; i <= loops; i++) {
			
			// **** compute this element in the series ****
			double p = 4.0 / (i * 2 + 1);
			
			// **** update value ****
			if (i % 2 == 0)
				pi += p;
			else
				pi -= p;
		}
				
		// **** return the value of Pi ****
		return pi;
	}

We start with a value of zero for Pi and loop the specified number of times adding and subtracting values. The idea is that the series will converge to the actual value of Pi. Note that Pi as far as we know does not have a repeating sequence. You just need to use a value with enough precision for your use case.

	// **** Nilakantha infinite series ****
	public double computeNSeries(int loops) {
		
		// **** ****
		double 	pi 	= 3.0;
		boolean add = true;

		// **** loop computing Pi ****
		for (int i = 1; i <= loops; i++) {

			// **** compute this element in the series ****
			double p = 4.0 / (1.0 * (i * 2) * (i * 2 + 1) * (i * 2 + 2));

			// **** update value ****
			if (add)
				pi += p;
			else
				pi -= p;
			
			// **** update flag ****
			add = add ? false : true;
		}

		// **** return Pi ****
		return pi;
	}

This method implements a different infinite series. As in the previous one it generates terms and add or subtracts them as it loops.

The next method does not use a loop. It computes the value using a formula. For simplicity I used the number of loops. A random value would work fine.

	// **** using a limit ****
	public double usingLimit(int largeNum) {
				
		// **** large number converted to radians ****
		double rad = Math.PI / largeNum;
		
		// **** compute Pi ****
		double pi = largeNum * Math.sin(rad);
				
		// **** return Pi ****
		return pi;
	}

In the last implementation we also use a formula. Given that the expected argument has to be in the range <0.0 : 1.0> we pass to it a randomly generated number in the specified range. You can see that the formula is indirectly dependent in the value of Pi via the Math.asin() calls.

	// **** using a number between 0 and 1 ****
	public double usingRandNum(double x) {
		
		// **** ****
		System.out.println("    x: " + x);

		// **** compute Pi ****
		double pi = 2.0 * (Math.asin(Math.sqrt(1.0 - (x * x))) + Math.asin(x));
		
		// **** ****
		return pi;
	}

OK, that is it for this post. If you follow my blog you will see one of the methods in this post used to generate Pi. The difference being that I used Java in this post and will use JavaScript in the next.

You can find my code in my GitHub repository.

If you have comments or questions regarding this or any other post in this blog, or if you would like me to help with any phase in the SDLC (Software Development Life Cycle) of a product or service, please do not hesitate and leave me a note below. Requests for help will remain private.

Keep on reading and experimenting. It is the best way to learn!

John

Follow me on Twitter:  @john_canessa

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.