Pi Day (3/14) is in a couple of days so I want to wish everyone a Happy Pi Day 2020! It’s great to see that 55% of American’s plan to celebrate and many will be eating pie or pi-themed food (whatever that is).

My work colleague and basketball buddy Stan sells a nerd t-shirt here: 314 Digits of Pi.py. It has the Python code on the front and the results on the back. I “won” one of these at our annual White Elephant gift exchange in December. Even though the Amazon Best Sellers Rank is #12,306,667 in Clothing, Shoes & Jewelry, I really like it!

I’ve been staring at the code backward in the mirror for a number of months:

This got me wondering what this algorithm would look like in Clojure?

The first pass on the port was pretty straight forward, but I think it’s worth noting some of the subtle differences. All of the code is here: pi-digits.

Here’s the original Python code and result:

pi.py import math def pi(): r = 4*(4*arccot(5) - arccot(239)) return str(r)[0] + '.' + str(r)[1:-5] def arccot(x): total = power = 10**319 // x divisor = 1 while abs(power) >= divisor: power = -power // x**2 divisor += 2 total += power // divisor return total print("314 digits of Pi " + pi()) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import math def pi ( ) : r = 4 * ( 4 * arccot ( 5 ) - arccot ( 239 ) ) return str ( r ) [ 0 ] + '.' + str ( r ) [ 1 : - 5 ] def arccot ( x ) : total = power = 10 * * 319 / / x divisor = 1 while abs ( power ) >= divisor : power = - power / / x * * 2 divisor += 2 total += power / / divisor return total print ( "314 digits of Pi " + pi ( ) )

result $ python3 pi.py 314 digits of Pi 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593344612847564823378678316527120190914564856692346034861045432664821339360726024914127372458700660631 1 2 $ python3 pi .py 314 digits of Pi 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593344612847564823378678316527120190914564856692346034861045432664821339360726024914127372458700660631

For the interested, here’s an explanation of the calculation of Pi using fixed point math for speed improvements: Pi – Machin: The Machin formula, developed by John Machin in 1706 (!), is:



And here’s a Clojure version that returns the identical result:

core.clj (ns pi-digits.core (:gen-class) (:require [clojure.math.numeric-tower :as math])) (defn pow [b e] (math/expt b e)) (defn abs [n] (math/abs n)) (defn arccot [x] (let [power (atom (quot (pow 10 319) x)) total (atom @power) divisor (atom 1)] (while (>= (abs @power) @divisor) (do (swap! power #(quot (- %) (pow x 2))) (swap! divisor #(+ % 2)) (swap! total #(+ % (quot @power @divisor))))) @total)) (defn pi "Calculate Pi to 314 digits" [] (let [r (str (* 4 (- (* 4 (arccot 5)) (arccot 239))))] (str (subs r 0 1) "." (subs r 1 315)))) (defn -main [& _] (println "314 digits of Pi" (pi))) 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 ( ns pi-digits . core ( : gen-class ) ( : require [ clojure . math . numeric-tower : as math ] ) ) ( defn pow [ b e ] ( math / expt b e ) ) ( defn abs [ n ] ( math / abs n ) ) ( defn arccot [ x ] ( let [ power ( atom ( quot ( pow 10 319 ) x ) ) total ( atom @ power ) divisor ( atom 1 ) ] ( while ( >= ( abs @ power ) @ divisor ) ( do ( swap ! power # ( quot ( - % ) ( pow x 2 ) ) ) ( swap ! divisor # ( + % 2 ) ) ( swap ! total # ( + % ( quot @ power @ divisor ) ) ) ) ) @ total ) ) ( defn pi "Calculate Pi to 314 digits" [ ] ( let [ r ( str ( * 4 ( - ( * 4 ( arccot 5 ) ) ( arccot 239 ) ) ) ) ] ( str ( subs r 0 1 ) "." ( subs r 1 315 ) ) ) ) ( defn -main [ & _ ] ( println "314 digits of Pi" ( pi ) ) )

One thing I discovered is Python’s use of an arbitrary-precision integer implementation that allows 10 319 to actually have 319 digits of precision. The use of Java’s double power implementation, ( Math / pow b e ) in Clojure, will not work because a double value can only represent about ± 10 308 . Getting the required precision required the use of the clojure/math.numeric-tower library and the ( math / expt b e ) function (integer implementation when e is a positive value).

to actually have 319 digits of precision. The use of Java’s double power implementation, in Clojure, will not work because a value can only represent about ± 10 . Getting the required precision required the use of the clojure/math.numeric-tower library and the function (integer implementation when e is a positive value). The Clojure quot[ient] does the same thing as Python’s floor division (//) function.

One problem with the arccot (arc-cotangent) implementation is that it just duplicates the Python logic and is not idiomatic Clojure. Instead of coding this in a non-functional style, i.e. using mutable state (atom), let’s create a functional version:

arccot-recur (defn arccot-recur [x] (loop [power (quot (pow 10 319) x) divisor 1 tot 0] (let [total (+ tot (quot power divisor))] (if (>= (abs power) divisor) (recur (quot (- power) (pow x 2)) (+ divisor 2) total) total)))) 1 2 3 4 5 6 7 8 9 ( defn arccot-recur [ x ] ( loop [ power ( quot ( pow 10 319 ) x ) divisor 1 tot 0 ] ( let [ total ( + tot ( quot power divisor ) ) ] ( if ( >= ( abs power ) divisor ) ( recur ( quot ( - power ) ( pow x 2 ) ) ( + divisor 2 ) total ) total ) ) ) )

We use loop/recur for a recursive implementation. This has the benefit of tail-call optimization (TCO). Here are the execution times (average of 10 runs) for calculating Pi with the three implementations:



Method Digits

Time in seconds 10,000 50,000 100,000 200,000 python 0.158 3.74 14.9 60.3 clojure-while 0.260 5.14 19.8 78.6 clojure-recur 0.252 5.10 19.8 78.5

Python is certainly faster, but the purpose here was not to compare computation speed. It was to get a Clojure version of the t-shirt made! Who’s the real nerd now? 🙂