Update: I revisited the calculation of Pi with even more digits.
In the United States today, March 14th is π day (3rd month, 14th day, or 3/14). Traditions involve eating pie, the pastry, and calculating Pi, the irrational mathematical constant.
In Delphi, Pi is included as an intrinsic function. If you look in the System unit there are references, but no definition. It has a floating point value of 3.141592653589793238 (although you don’t usually see the last few digits). This is more accuracy than even NASA uses. But for theoretical mathematics and certain definitions of fun, we can do better.
As an irrational number, the decimal portion goes on forever without repeating. So when you calculate Pi you go through iterations improving your accuracy, or the number of digits precision. Google recently calculated 100 Trillion digits of Pi. It took 157 days, 23 hours, 31 minutes and 7.651 seconds on a 128 vCPUs with 864 GB RAM, 515 TB storage, and 82 PB of I/O. This was an opportunity to show off their cloud computing infrastructure. So the practical among us would just download the digits we need (as long as it is less than 100 trillion).
When it comes to calculating Pi there are a number of different algorithms. Many of us may have memorized a few digits, or some other approximation. But what if we want to calculate more? I poked around the web and even consulted Wolfram Alpha and ChatGPT. Here are a few examples I put together for you.
Table of Contents
Leibniz’s Formula for Pi
Named after Gottfried Leibniz, this formula comes from approximately the 14th century. I found a few examples in Delphi, and massaged them into the following implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function LeibnizPi(const Iterations: NativeUInt): Extended; begin Result := 0; var k: Extended := 1; for var I := 0 to Iterations do begin if odd(I) then Result := Result - 4 / k else Result := Result + 4 / k; k := k + 2; end; end; |
This ends up being rather slow and inaccurate. Running it for MaxInt iterations results in a value of 3.14159265312413, only accurate to 9 decimal places.
Talking to Nilkantha
My notes say this is Nilkantha, but now I’m not sure. It was a bit more accurate than Leibniz, thus saving some time.
1 2 3 4 5 6 7 8 9 10 11 12 |
function NilkanthaPi(const Iterations: NativeUInt): Extended; begin Result := 3; var n: Extended := 2; var sign := 1; for var I := 0 to Iterations do begin Result := Result + sign * (4 / (n * (n + 1) * (n + 2))); sign := sign * -1; n := n + 2; end; end; |
1000 iterations is accurate to 9 decimal places and 11,000 iterations is accurate to 12 places.
If someone recognizes this as a different algorithm, please let me know.
The Bailey–Borwein–Plouffe Pi Forumula
This formula was discovered in 1995 by Simon Plouffe. The BBP formula gives rise to a spigot algorithm for computing the nth base-16 (hexadecimal) digit of π (and therefore also the 4nth binary digit of π). When I started trying to convert it to Delphi code I decided to give ChatGPT a shot, and it got me really close to working code. A little clean up gave me the following:
1 2 3 4 5 6 7 8 9 10 |
function BaileyBorweinPlouffePi(const Digits: NativeUInt): Extended; begin Result := 0; for var I := 0 to Digits do begin Result := Result + 1 / power(16, I) * ((4 / (8 * I + 1)) - (2 / (8 * I + 4)) - (1 / (8 * I + 5)) - (1 / (8 * I + 6))); end; end; |
This one is very fast, and indicating 10 digits is more than enough.
Enter Rudy Big Decimals
What if we want more than 10 digits of accuracy? Well, the late great Rudy Velthuis created a BigNumbers library which is available on GitHub. It includes BigIntegers, BigDecimals, and BigRationals, along with a number of other useful libraries. I converted the BBP algorithm to use BigDecimals and ended up with the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function BigBaileyBorweinPlouffePi(const Digits: NativeUInt): BigDecimal; begin const sixteen = BigDecimal.Create(16); Result := BigDecimal.Zero; Result.DefaultPrecision := Digits; for var I := 0 to Digits do begin var term1 := BigDecimal.Create((4 / (8 * I + 1)) - (2 / (8 * I + 4)) - (1 / (8 * I + 5)) - (1 / (8 * I + 6))); var term2 := BigDecimal.Divide(1, sixteen.IntPower(I), Result.DefaultPrecision); Result := Result + BigDecimal.Multiply(term1, term2); end; end; |
It is still very fast, and outputs very long numbers, but unfortunately I was still only able to get about 20 digits accurately.
Conclusion
The pi built into Delphi is more than enough for most uses, but it is nice to look at the options for generating pi from scratch with Delphi. I’ve posted all my code on GitHub in a Gist, and will update it if I make any improvements based on more testing or feedback. Also, if you find yourself needing bigger Integers or Decimals, check out Rudy’s library.
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition