
A Million Digits of Pi in 9 Lines of JavaScript - ajennings
http://ajennings.net/blog/a-million-digits-of-pi-in-9-lines-of-javascript.html
======
dmurray
> this simple one still converges at about 0.6 decimal digits per term.

Quick proof of this: as the number of terms _n_ in the sum goes to infinity,
the ratio of each term to the previous one is approximately 1/4 - the first
factor contributes _m /(m+1)_, the second _q /(q+2)_ for some _m_ and _q_ that
go to infinity along with _n_ , the third contributes 1/4.

If we counted base 4, then the value of each digit would be on average 1/4 of
the previous one, certainly for a normal number like pi. But we count base 10,
so we get _log_10 4_ decimal digits every time we get one base-four digit.
Which is very close to 0.6.

~~~
mycall
I wonder if base 4 math is how people recite this out loud.

------
jrochkind1
His demo page, in my Chrome, if I enter 10000, it takes about 2 seconds to
finish with 10k digits.

But if I enter 100k, it takes 30 seconds to get to reporting 10k digits worth
of progress.

Hmm. Have to think about that one. Just cause it's asking JS to do comparisons
of much larger numbers?

~~~
zamadatix
I'd bet a good portion of the difference is between rendering the additional
digits as it goes.

~~~
ajennings
I didn't note timing numbers, but rendering digits in hex was basically
instantaneous, even when doing a million (decimal) digits.

Rendering the digits in decimal was significant delay (like 40 seconds for a
million digits). That's why it just shows the hex until the very end, and it
shows how long the decimal conversion takes.

~~~
zamadatix
You sure you don't mean converting digits to hex instead of rendering? The
constant rendering of the hex digits as the work progresses results in a ~3x
slowdown for 100k digits in my testing (see my other comment in this tree).

~~~
ajennings
Maybe you're right. I know displaying in hex was way faster than displaying in
decimal and seemed fast enough, so I stopped there. I didn't test rendering
speed specifically.

Doing a million digits in the console (with no status updates) took about an
hour and doing it in that page took just over two hours.

------
tombert
Wow, I wonder why Firefox is so much slower? Quantum seems pretty zippy
overall, but maybe that's largely due to fast rendering speeds?

Does anyone on the Spidermonkey team have some insight?

~~~
war1025
Looks like they are using big integers, which I'd guess isn't something that
comes up a lot, so probably hasn't been optimized for too much.

~~~
ehsankia
It is pretty new, and as you mention not many websites use them yet, so I'm
sure there are more important optimizations the Firefox team is focusing on
for now.

------
mourner
Alternatively, you can generate Pi digits one by one in a streaming way:
[https://observablehq.com/@mourner/calculating-pi-
digits](https://observablehq.com/@mourner/calculating-pi-digits)

------
wasnthere
JS Error `No identifiers allowed directly after numeric literal` when running
[http://ajennings.net/pi.html](http://ajennings.net/pi.html) on Mac OS Mojave
10.14.6, Safari 12.1.2 (14607.3.9)

~~~
MildlySerious
Safari does not support BigInt yet it seems.

~~~
oehtXRwMkIs
Safari seems to be consistently behind these days, not sure why.

~~~
LeoPanthera
Apple seems to be the only browser developer that makes user-centric changes
first, not developer-centric.

I'm OK with that.

~~~
recursive
It seems user-centric to you know... make the browser work for what users what
to use it for. Like generating digits of pi.

~~~
nacs
Yes I'm sure there's a lot of consumers that want to calculate large amounts
of digits of Pi in their browser..

~~~
recursive
I didn't say that. You're moving goalposts anyway. Calculating pi isn't the
only use for big ints. The more practical application is maybe crypto or
something.

But _some_ users want to do it.

------
phonebucket
A fun related thread on math stackexchange which has several examples of
series which converge quickly to pi:
[https://math.stackexchange.com/questions/14113/series-
that-c...](https://math.stackexchange.com/questions/14113/series-that-
converge-to-pi-quickly)

edit: There also some nice formulae for quick convergence in this article:
[https://julialang.org/blog/2017/03/piday](https://julialang.org/blog/2017/03/piday)

------
AdmiralAsshat
Can someone explain the logic behind the evolution of the fractions at each
stage? I see that (1/4) becomes (1/4^2) and (1/4^3), but it's not obvious to
me how (1/3)->(1/5)->(1/7) flows (odds? primes?), or (1/2) -> (13/24) ->
(135/246).

EDIT: I understand now, the numerator on the first term is ascending odds and
the denominator is ascending evens. Thanks for everyone's help!

~~~
joeyrideout
Remove the leading 3 and the trailing term of 1/4 increasing in power.

You are left with this (in the 4th line):

1 3 5 1

\- - - -

2 4 6 7

The pattern I see is that, starting from the top left and reading numerator,
denominator, numerator, denominator and so on gives: 1 2 3 4 5 6 7 if you
ignore the last numerator.

I may have it wrong, but that looks like the pattern.

~~~
mef51
4th step is 1357/2468

~~~
joeyrideout
Is that simplified? I'm just going off of this image:
[http://ajennings.net/blog/images/formula.png](http://ajennings.net/blog/images/formula.png)

~~~
mef51
me too, the pattern to me is odds on the numerator and evens in the
denominator, and the second fraction goes 1/3, 1/5, 1/7, 1/9...

i think we're saying the same thing i misunderstood your comment. 1357/2468 is
the first fraction of the NEXT line that isnt in the image

~~~
joeyrideout
Aha, yes, I edited my post now to say 4th _line_ not 4th _step_. Leave it to
HN to get an off-by-one error :)

------
bakul
> this simple one still converges at about 0.6 decimal digits per term.

The Chudnovsky brothers’ algorithm computes 14.18... digits per term. Its
implementation in Scheme is only about a couple dozen lines of code. It
computes a million pi digits in about 17.5 seconds on raspberry pi 4 in Gambit
Scheme (57 seconds on the original raspberry pi, IIRC).

------
52-6F-62
For those of us behind work proxies that dumbly think this site is "domain
parking" or worse:

[http://archive.is/hUo6Q](http://archive.is/hUo6Q)

~~~
ajennings
Personal domain. I haven't used it for much over the years.

The hosting is static pages on S3.

I wonder if there's anything I could do to avoid the domain getting flagged.

Maybe it's because the root page on the domain is just a quote.

~~~
52-6F-62
Not sure. Where the company I work for operates they have some very weird
proxy setup (not my company’s rule but the host). Largely it seems only
effective at blocking legitimate content for nonsense reasons. My own site
gets blocked on occasion, though I’ve lazily let the cert expire. That reminds
me....

Anyway I wouldn’t worry too much. It’s likely just stodgy corporate
environments that put those kinds of controls in place.

------
LeoPanthera
On any standard unix system with bc installed - it's preinstalled on most of
them, you can calculate pi to $n digits using bc:

bc -l <<< "scale=$n; 4*a(1)"

~~~
mjcohen
The algorithm seems to be at least quadratic in the length. On a 2014 i7 Mac
mini, (n, time(sec)) = (1000, 0.29), (2000, 1.65), (4000, 9.70), (8000,
58.42).

~~~
notfashion
I think this section on Wikipedia is relevant:
[https://en.wikipedia.org/wiki/Approximations_of_%CF%80#Grego...](https://en.wikipedia.org/wiki/Approximations_of_%CF%80#Gregory%E2%80%93Leibniz_series)

------
russellbeattie
I was going to make a joke about just writing Math.PI.toFixed(10 * * 6), but
it turns out that Number.toFixed() only supports up to 100 places AND Math.PI
stops at the 50th place. Still amusing to me. Also, BigInt numbers can't be
combined with non BigInt without conversion. I haven't had cause to use them
yet, so I didn't know that.

Learned three new things making a dumb HN joke... not bad!

------
userbinator
I thought it would be the "spigot" algorithm, which is based on Bellard's
formula (yes, _that_ Bellard) and yields similarly small programs:

[http://numbers.computation.free.fr/Constants/TinyPrograms/ti...](http://numbers.computation.free.fr/Constants/TinyPrograms/tinycodes.html)

------
sp332
This also takes a lot of RAM. Keep an eye on it during longer executions or
the swapping could make your box pretty unusable.

~~~
ajennings
It should take 415kB to store a million digit number, and the algorithm only
needs to keep two of them. So, 1MB total to calculate a million digits of pi.
I wonder if there are a lot of temporary allocations that build up until they
get garbage collected.

~~~
sp332
Not sure what it is exactly, but forcing a garbage collection (using the
"minimize memory usage" button in about:memory) doesn't make any difference.

------
andig
I still can't work out how the formula shown translates to the code given. Any
hints?

------
adossi
I'm interested to see how the computation time would progress with the recent
V8 memory enhancements.

------
foxes

      let y=3n*(10n**1000020n);
      const f=(i,x,p)=>{(x>0)?f(i+2n,x*i/((i+1n)*4n),p+x/(i+2n)):p/(10n**20n)} 
      console.log(f(1n,y/8n,y));
    

Not sure if I can golf it anymore

~~~
minitech
That version already doesn’t work… once you fix the arrow function, there’s
also the issue that most engines today don’t support proper tail calls, so
nothing recursive will be portable. (But if it did, you could save a lot of
characters by dropping unnecessary parentheses, expanding (i+1n)×4n to
4n×i+4n, replacing the const with a comma, removing semicolons…)

~~~
sqnguyen
this is my perl golf version of the same series. You can probably can do
something similar with js:

map$l+=(-1) __$_ /(1-$_ _2)_ 4,1..<>;die$l

~~~
NieDzejkob
Doesn't work for me [0]. Perhaps HN messed it up somehow?

[0]:
[https://tio.run/##K0gtyjH9/z83sUAlR9tWQ9dQUyVeX8NQVyXeSNNEx1...](https://tio.run/##K0gtyjH9/z83sUAlR9tWQ9dQUyVeX8NQVyXeSNNEx1BPz8bOOiUzVSXn/38A)

~~~
sqnguyen
I guess so...
[https://tio.run/##K0gtyjH9/z83sUAlR9tWQ9dQU0tLJV5fw1BXJV7LSF...](https://tio.run/##K0gtyjH9/z83sUAlR9tWQ9dQU0tLJV5fw1BXJV7LSFPLRMdQT8/GzjolM1UlR@H/f0MDIAAA)

------
ilovepeppapig
It will be much faster if you don't update the HEX values all the time (they
are not that interesting anyway).

~~~
ajennings
Displaying hex is quite fast (compared to trying to display decimal), though
you're right that it might add up to a noticeable amount of time in the end. I
did the full million digit calculation on that sample page on the same
computer and it took a little over two hours (so 2.1x slowdown) but at that
point I was also adding a 5ms delay every 100 terms.

It is fun to scroll down and watch the "cutoff", where digits above that are
not changing and digits below that are. That's just as fun in hex as it is in
decimal. But yes, maybe I should add an option to turn that off.

------
hervature
What is the actual equation? They just list the first couple of terms.

~~~
mkl
Continuing the pattern it's

    
    
      $\pi = 3 + \sum_{k=1}^\infty 3 \frac{(2k-1)!!}{(2k)!!} \frac{1}{2k+1} \frac{1}{4^k}$ [1].
    

n!! is double factorial, the product of odd or even numbers up to n (depending
on whether n is odd or even) [2].

Edit: added a simpler series from
[https://math.stackexchange.com/a/14116](https://math.stackexchange.com/a/14116):

    
    
      $\pi = \sum_{k=0}^{\infty} \frac{(2k)!!}{(2k+1)!!} \left(\frac{1}{2}\right)^{k-1}$
    

[1] [https://imgur.com/a/YtA8kUx](https://imgur.com/a/YtA8kUx)

[2]
[https://en.wikipedia.org/wiki/Double_factorial](https://en.wikipedia.org/wiki/Double_factorial)

------
dlojudice
Is there a BigDecimal, Money, or similar coming to JS?

~~~
mark-r
You could probably emulate them. For example, Money would just be BigInt/100.

------
paulpauper
infinite series ..an amazing discovery

------
craftinator
wget "[https://www.piday.org/million/"](https://www.piday.org/million/")

Got you beat, js.

~~~
reificator
Until you go to examine it and realize it's infinite scrolling via JS and
spend all your time trying to hack around that. (Or you just grab the URL out
of the request and fetch it a few times directly)

