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.
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
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.
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.