Here’s a guest blog post by Jim McKeeth, Embarcadero MVP and US Director of GDK Software. To celebrate the recent π day 2024 with a Delphi flavor I revisited the previous sample I made with Rudy’s Big Numbers library adding the functionality to view the digits of π in real time as they are being generated. Previously it would generate any number of digits you wanted, but you could only see them when it was all done.
Table of Contents
What is Rudy’s Big Numbers Library?
Originally developed by the late Rudy Velthuis, the library adds support to Delphi for Big Integer, Big Decimal, and Big Rational numbers to Delphi. How big? Well, how much memory do you have? There really isn’t an upper limit.
The library is really easy to work with, and performs surprisingly well. There are a variety of samples, demos, and unit tests. The BigRational type is marked as experimental, and BigIntegers tends to perform better than BigDecimals.
How to Generate Pi with Delphi?
Last year I created the sample to calculate Pi with Delphi. It supports both the Chudnovsky and Bailey Borwein Plouffe (BBP) algorithms. Since BBP is faster, and it is a spigot that generates the digits in sequence, I thought it made more sense to add a call back to that algorithm. The callback returns 64 digits at a time, but you could easily modify that chunk size.
Watching the Digits of Pi in Real-Time
I find watching the digits of Pi appear both Zen like and exciting. Currently only the console app supports the callback function, but I plan to update the FireMonkey app in the future too.
The Bailey-Borwein-Plouffe Pi Code in Delphi
The updated code with the callback function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
function BBPpi(Places: UInt64; CallBack: TChunkCallback = nil): TDigits; // Bailey-Borwein-Plouffe begin SetLength(Result, Places); var idx: Uint64 := 0; var q := BigInteger.One; var r := BigInteger.Zero; var t := BigInteger.One; var k := BigInteger.One; var n := BigInteger.Create(3); var l := BigInteger.Create(3); var buffer: TDigits; SetLength(buffer, CallbackChunkSize); var bufferIdx: Integer := 0; while true do begin if 4*q+r-t < n*t then begin result[idx] := n.AsInt64; // It is just a byte inc(idx); if Assigned(Callback) then begin buffer[bufferIdx] := n.AsInt64; inc(bufferIdx); // Check if buffer is full, then call callback and reset bufferIdx if bufferIdx = CallbackChunkSize then begin Callback(buffer); // Call the callback with the buffer bufferIdx := 0; // Reset buffer index end; end; if idx >= places then break; var newR := 10 * (r - n * t); n := (10 * (3 * q + r)) div t - 10 * n; q := q * 10; r := newR; end else begin var newR := (2 * q + r) * l; var newN := (q * (7 * k)+2+(r * l)) div (t * l); q := q * k; t := t * l; l := l + 2; k := k + 1; n := newN; r := newR; end; end; // Handle remaining buffer if (bufferIdx > 0) and Assigned(CallBack) then begin SetLength(buffer, bufferIdx); // Resize buffer to actual used size before callback CallBack(buffer); end; end; |
And here is an example of calling the code and then outputting the digits one at a time to the console
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
var firstChunk: Boolean = True; procedure WritelnCallBack(Chunk: TDigits); begin var digits: String; if firstChunk then begin digits := DigitsToString(Chunk).Insert(1,'.'); firstChunk := False; end else digits := DigitsToString(Chunk); // slow down the output for demonstration purposes for var i := low(digits) to High(digits) do begin Write(digits[i]); sleep(100); end; end; const Places = 10000; begin BBPpi(Places, WritelnCallBack); Writeln; end. |
It currently uses an anonymous method reference, which I know introduces a slight amount of overhead, but I’ve not benchmarked it to see how much or if it is worth changing the callback type. I figured since the purpose was to view the digits then a little slow down was necessary.
I also added a unit test to validate that the output from the callback matched the result of the function call.
1 2 3 4 5 6 7 8 9 10 11 |
procedure BBPpiTest.CompareCallbackToResult; begin var CallBackString := ''; var CallBackStringBuilder: TChunkCallBack := procedure(Chunk: TDigits) begin CallBackString := CallBackString + DigitsToString(Chunk); end; var calcPi := DigitsToString(BBPpi(100, CallBackStringBuilder)); Assert.AreEqual(calcPi, CallBackString); end; |
Where Can I Get that Cool Vaporwave Athena Pi Day Wallpaper?
I’m glad you asked! I upscaled it to 4K, and there are a few variations….
Do you want to try some of these examples for yourself? Why not download a free trial of the latest version of RAD Studio with Delphi?
This article was written by an Embarcadero MVP.
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition