Jon Wire

A Software Engineer exploring the world with code.

Calculating π (pi) because we can

A quick reminder: What is \(\pi\)?

\(\pi\) (pi, said like "pie") is the greek letter we use to represent the ratio between the circumference of a circle (the distance around it) and its diameter (the distance across it through the center point). If we look at a circle where we call the circumference \(C\) and the diameter \(d\), like this:

circle with circumference and diameter labeled

\(\pi\) would be defined as \(C \over d\). We know that the value of \(\pi\) is a constant of roughly 3.14159 (the size of the circle doesn't matter). If the diameter is 1, the circumference will be approximately 3.14159. If the diameter is 3, the circumference will be approximately 9.42477 (That's \(\pi \times 3\).). The unit doesn't matter. You can imagine this is measured in feet, centimeters, tacos, or whatever else circles in your household are measured in.

This is old news to most of us.

But today, on \(\pi\) day (3/14), I'm taking some time to honor \(\pi\) by calculating it. I will calculate it for a unit circle (a circle whose radius is 1 and diameter is therefore 2). I will calculate it for a non-unit circle. And, I will then, hopefully, rest confidently for the remainder of the year, knowing that \(\pi\) is \(\pi\), now and forever. (At least until next year, when I may revisit it to ensure that \(\pi\) has not grown up!)

So, how do we do this?

Presumably, there are a lot of ways to calculate \(\pi\). Today, I'm aiming at simplicity to ensure that what we calculate is a clear and obvious approximation of \(\pi\). No funny looking or clever math today. (That stuff is generally beyond me anyway.) That said, I know that I can accurately measure a straight line. And, if we look at a circle and imagine drawing smaller and smaller lines around the circumference, it looks like the sum of the segments becomes closer and closer to the actual circumference of the circle.

Visually, this looks like a solid approach. We can see that our estimate will be a little short. But, if we make our segments even smaller, I suspect the difference will be negligible.

So, here are the conceptual steps in my plan:

  1. Create a circle of a known diameter.
  2. Walk the circumference of a quadrant of the circle, measuring distance as I go.
  3. Multiply the sum of the distances of that quadrant by 4 to get the circumference.
  4. Divide the measured circumference by known the diameter.

To do this, I'll note that, by definition, every (x,y) coordinate in a circle is a exactly the circle's radius (half the diameter) away from the center, which we'll place at (0,0). The top edge of the circle is therefore at (0,r), and the right edge is at (r,0). Within that quadrant we can find (x,y) pairs by picking an x between 0 and d and finding the y to make the point r units from the center at (0,0).

I can then calculate the distance traveled from the previous coordinate and add it to the total distance traveled. In the image above, I show the stepping between coordinates as being quite large. As noted earlier, we'll take smaller steps.

To make this all work, I'll divide the quadrant into segments evenly along the x axis. This won't be perfect, but it will be easy to understand, and I'm pretty optimistic this will be "good enough". (But, we'll find out together!) As I noted above, we will find the corresponding y with respect to x using the radius. Specifically, we want each "waypoint" to be exactly "1 radius" away from (0,0).

This is pretty straightforward if we start with the Pythagorean Theorem, normally used for finding the length of the hypotenuse, and we solve for \(y\) instead of the hypotenuse (which will be the radius \(r\) in our case): $$r^2 = x^2 + y^2$$ $$r^2 - x^2 = y^2$$ $$\sqrt{r^2 - x^2} = y$$

As JavaScript, this can simply be:

function findY(radius, x) {
	return Math.sqrt(radius**2 - x**2);
}

And, for the distance between two points along our walk around the circle, we just need to apply the same theorem, solving for the hypotenuse this time, and bearing in mind that the lengths of each side are just the differences between the x and y coordinates. $$d^2 = (x_1-x_2)^2 + (y_1-y_2)^2$$ $$d = \sqrt{(x_1-x_2)^2 + (y_1-y_2)^2}$$

As code, this can also be pretty simple:

function distance(a, b) {
	return Math.sqrt((a.x - b.x)**2 + (a.y - b.y)**2);
}

With these tiny building blocks out of the way, putting these together to iterate over the segments of the upper right quadrant of a circle is pretty straightforward. We just need to go from x = 0 to x = radius in small increments. Because these steps will not necessarily be integers, we'll derive x by counting which "step" we're on as a percentage of maximum value x can be.

If we were to highly decompose our work, we could make a function to calculate x based on our numberOfSteps, currentStep, and radius. It might look like this:

function findX(radius, numberOfSteps, currentStep) {
	// what "percentage" of the way through the quadrant are we?
	const pct = currentStep / numberOfSteps;

	// if we divide the radius up into equal steps along the X axis,
	// what `x` coordinate lives there?
	return radius * pct;
}

Then, putting these all together into a loop to sum the segments is pretty straightforward.

function measure(radius, numberOfSteps) {
	// before doing anything, we have accumulated
	// zero distance.
	let sum = 0;

	// once we get going, we'll need to keep track of
	// the previous point to compute the length of the
	// segment the "current" point represents.
	let previousPoint = undefined;

	// now, we walk through each point. Leveraging the
	// util functions we created makes this pretty trivial.
	for (let step = 0; step <= numberOfSteps; step++) {
		const x = findX(radius, numberOfSteps, step);
		const y = findY(radius, x);
		const currentPoint = { x, y };
		if (previousPoint) {
			sum += distance(currentPoint, previousPoint);
		}
		previousPoint = currentPoint;
	}

	// circumference is `2 * radius`. so, this would be
	// `(sum * 4) / (radius * 2)`, but we can simplify it
	// to this:
	return (sum * 2) / radius;
}

Giving this a quick run, even on a small number of steps, it's clear that this estimation is pretty close to the well-known \(\pi\) constant we've all heard so much about. (Maybe this isn't a ruse after all.) And, as we start increasing the number of steps, it clearly converges on the same value JavaScript believes \(\pi\) to be:

console.log(measure(1, 1));      // 2.8284271247461903
console.log(measure(1, 10));     // 3.132264618516503
console.log(measure(1, 100));    // 3.1412985671602183
console.log(measure(1, 1000));   // 3.141583356349993
console.log(measure(1, 10000));  // 3.1415923595933752
console.log(Math.PI);            // 3.141592653589793

And, of course, using a different yields roughly the same results, which aligns with the legend that \(\pi\) is a constant that applies to all circles.

console.log(measure(5, 1));      // 2.8284271247461903
console.log(measure(5, 10));     // 3.1322646185165026
console.log(measure(5, 100));    // 3.141298567160219
console.log(measure(5, 1000));   // 3.1415833563499946
console.log(measure(5, 10000));  // 3.1415923595933695
console.log(Math.PI);            // 3.141592653589793

Let's see how close our approximation is:

const diff = Math.PI - measure(1, 10000);
console.log(diff.toFixed(10));  // 0.0000002940

And, of course, we can get very to JavaScript's constant by increasing the step count. On my machine, this calcuation still feels instantaneous.

const diff = Math.PI - measure(1, 10_000_000);
console.log(diff.toFixed(15));  // 0.000000000009354

So, there we have it. We have experimental evidence that \(\pi\) does indeed seem to equal the well-known constant that inspires so many of us to eat pie on March 14th (3/14) every year. In honor of \(\pi\) today, enjoy some piebut before you eat it, remember to measure the circumference and confirm that it is \(\pi\) times longer than the diameter.

Or even better, run an experiment in code for yourself!

Next year, I will likely confirm again that \(\pi\) has not sneakily changed on us. And I will perhaps look at other ways of computing \(\pi\). Perhaps more efficiently, more precisely, or ... perhaps just more differently.

If you enjoyed anything about this article, subscribe so you don't miss out!

* indicates required

Intuit Mailchimp

Published: 3/14/2025.
Topics: math, play, pi