8

Is there a way to calculate pi in Javascript? I know there you can use Math.PI to find pie like this:

var pie = Math.PI;
alert(pie); // output "3.141592653589793"

but this is not accurate. What I want is to be able to calculate it, to have as many digits as you want, not anything like pie = 3.141592.... But still, what I want is not have just have some more digits, but as much as you can (like having one thousand digits, but I need more).

Cai Haoyang
  • 192
  • 1
  • 3
  • 16

5 Answers5

13

Here is an implementation of a streaming algorithm described by Jeremy Gibbons in Unbounded Spigot Algorithms for the Digits of Pi (2004), Chaper 6:

function * generateDigitsOfPi() {
    let q = 1n;
    let r = 180n;
    let t = 60n;
    let i = 2n;
    while (true) {
        let digit = ((i * 27n - 12n) * q + r * 5n) / (t * 5n);
        yield Number(digit);
        let u = i * 3n;
        u = (u + 1n) * 3n * (u + 2n);
        r = u * 10n * (q * (i * 5n - 2n) + r - t * digit);
        q *= 10n * i * (i++ * 2n - 1n);
        t *= u;
    }
}

// Demo
let iter = generateDigitsOfPi();

let output = document.querySelector("div");
(function displayTenNextDigits() {
    let digits = "";
    for (let i = 0; i < 10; i++) digits += iter.next().value;
    output.insertAdjacentHTML("beforeend", digits);
    scrollTo(0, document.body.scrollHeight);
    requestAnimationFrame(displayTenNextDigits);
})();
div { word-wrap:break-word; font-family: monospace }
<div></div>
trincot
  • 317,000
  • 35
  • 244
  • 286
6

I found this code on this website:

mess = "";
Base = Math.pow(10, 11);
cellSize = Math.floor(Math.log(Base) / Math.LN10);
a = Number.MAX_VALUE;
MaxDiv = Math.floor(Math.sqrt(a));

function makeArray(n, aX, Integer) {
  var i = 0;
  for (i = 1; i < n; i++) aX[i] = null;
  aX[0] = Integer
}

function isEmpty(aX) {
  var empty = true
  for (i = 0; i < aX.length; i++)
    if (aX[i]) {
      empty = false;
      break
    }
  return empty
}

function Add(n, aX, aY) {
  carry = 0
  for (i = n - 1; i >= 0; i--) {
    aX[i] += Number(aY[i]) + Number(carry);
    if (aX[i] < Base) carry = 0;
    else {
      carry = 1;
      aX[i] = Number(aX[i]) - Number(Base)
    }
  }
}

function Sub(n, aX, aY) {
  for (i = n - 1; i >= 0; i--) {
    aX[i] -= aY[i];
    if (aX[i] < 0) {
      if (i > 0) {
        aX[i] += Base;
        aX[i - 1]--
      }
    }
  }
}

function Mul(n, aX, iMult) {
  carry = 0;
  for (i = n - 1; i >= 0; i--) {
    prod = (aX[i]) * iMult;
    prod += carry;
    if (prod >= Base) {
      carry = Math.floor(prod / Base);
      prod -= (carry * Base)
    } else carry = 0;
    aX[i] = prod
  }
}

function Div(n, aX, iDiv, aY) {
  carry = 0;
  for (i = 0; i < n; i++) {
    currVal = Number(aX[i]) + Number(carry * Base);
    theDiv = Math.floor(currVal / iDiv);
    carry = currVal - theDiv * iDiv;
    aY[i] = theDiv
  }
}

function arctan(iAng, n, aX) {
  iAng_squared = iAng * iAng;
  k = 3;
  sign = 0;
  makeArray(n, aX, 0);
  makeArray(n, aAngle, 1);
  Div(n, aAngle, iAng, aAngle);
  Add(n, aX, aAngle);
  while (!isEmpty(aAngle)) {
    Div(n, aAngle, iAng_squared, aAngle);
    Div(n, aAngle, k, aDivK);
    if (sign) Add(n, aX, aDivK);
    else Sub(n, aX, aDivK);
    k += 2;
    sign = 1 - sign
  }
  mess += "aArctan=" + aArctan + "<br>"
}

function calcPI(numDec) {
  var ans = "";
  t1 = new Date();
  numDec = Number(numDec) + 5;
  iAng = new Array(10);
  coeff = new Array(10);
  arrayLength = Math.ceil(1 + numDec / cellSize);
  aPI = new Array(arrayLength);
  aArctan = new Array(arrayLength);
  aAngle = new Array(arrayLength);
  aDivK = new Array(arrayLength);
  coeff[0] = 4;
  coeff[1] = -1;
  coeff[2] = 0;
  iAng[0] = 5;
  iAng[1] = 239;
  iAng[2] = 0;
  makeArray(arrayLength, aPI, 0);
  makeArray(arrayLength, aAngle, 0);
  makeArray(arrayLength, aDivK, 0);
  for (var i = 0; coeff[i] != 0; i++) {
    arctan(iAng[i], arrayLength, aArctan);
    Mul(arrayLength, aArctan, Math.abs(coeff[i]));
    if (coeff[i] > 0) Add(arrayLength, aPI, aArctan);
    else Sub(arrayLength, aPI, aArctan)
  }
  Mul(arrayLength, aPI, 4);
  sPI = "";
  tempPI = "";
  for (i = 0; i < aPI.length; i++) {
    aPI[i] = String(aPI[i]);
    if (aPI[i].length < cellSize && i != 0) {
      while (aPI[i].length < cellSize) aPI[i] = "0" + aPI[i]
    }
    tempPI += aPI[i]
  }
  for (i = 0; i <= numDec; i++) {
    if (i == 0) sPI += tempPI.charAt(i) + ".<br>";
    else {
      if (document.getElementById("cbCount").checked) addcount = " (" + (i) + ")";
      else addcount = "";
      if (document.getElementById("cbSpace").checked) thespace = " ";
      else thespace = "";
      if ((i) % 50 == 0 && i != 0) sPI += tempPI.charAt(i) + addcount + "<br>";
      else if (i % 5 == 0) sPI += tempPI.charAt(i) + thespace;
      else sPI += tempPI.charAt(i)
    }
  }
  ans += ("PI (" + numDec + ")=" + sPI + "<br>");
  ans += ("Win PI=<br>3.1415926535897932384626433832795<br>");
  t2 = new Date();
  timeTaken = (t2.getTime() - t1.getTime()) / 1000;
  ans += "It took: " + timeTaken + " seconds";
  var myDiv = document.getElementById("d1");
  myDiv.innerHTML = ans
}
<form name="" onsubmit="calcPI(this.t1.value);return false;">
  Number of Digits:<br>
  <input type="text" name="t1" id="t1" value="100" size="25" maxlength="25">
  <br>Add Count:
  <input type="checkbox" name="cbCount" id="cbCount" value="" checked="checked">
  <br>Add Spaces:
  <input type="checkbox" name="cbSpace" id="cbSpace" value="" checked="checked">
  <br>
  <input type="button" value="Calculate Pi" onclick="calcPI(this.form.t1.value)">
</form>
<div id="d1"></div>
trincot
  • 317,000
  • 35
  • 244
  • 286
  • 4
    @CaiHaoyang works the way that you finish some higher grades of math and programming, a bit of knowledge of JS and floating point errors, you go trough that script line by line getting some clue and taking notes :) – Roko C. Buljan Jun 10 '15 at 05:00
  • Without much study one can, I think, see that it does multiple precision arithmetic in radix 10¹¹ in arrays one longer than needed to hold the desired number of decimal digits. I suppose that leaves a fair margin for error, but I don’t know enough to be sure. I wonder how fast it is. – PJTraill Jun 11 '15 at 02:46
  • 1
    For what it is worth: this produces correct results up to the 63198th digit. The digits from the 63191st position onward are 1950140348..., and this code produces 19501403465... instead. This is expected, as the limits of floating point precision will kick in at a certain moment. – trincot Feb 14 '22 at 12:06
6

You can approximate the value of π through the use of Monte Carlo simulation. Generate a random X and Y each in the range [-1,1] Then the likelihood (X, Y) is in the unit circle centered at the origin is π/4. More samples yields a better estimate of its value. You can then estimate π by comparing the ratio of samples in the unit circle with the total number of samples and multiply by 4.

this.pi = function(count) {
    var inside = 0;

    for (var i = 0; i < count; i++) {
        var x = random()*2-1;
        var y = random()*2-1;
        if ((x*x + y*y) < 1) {
            inside++
        }
    }

    return 4.0 * inside / count;
}
Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
andand
  • 17,134
  • 11
  • 53
  • 79
  • What's the precision of such calculation? I mean at what point it becomes off? – Roko C. Buljan Jun 10 '15 at 04:58
  • 1
    Since it uses Monte Carlo simulation, it may not produce good results especially for a small number of trials. However the likelihood of good results increases with the number of trials. So, the precision is non-deterministic. – andand Jun 10 '15 at 05:00
  • 2
    Due to the semantics of floating point numbers, it is impossible to get a more accurate representation of pi than `Math.PI` without the use of a BigDecimal library. – Qantas 94 Heavy Jun 10 '15 at 05:11
  • What should be the value of `count` to have a very likely chance to get 14 decimal digits right (like `Math.PI` gives)? I tried with values up to 1e9, but then still only the first 3 to 4 decimal digits are stable (3.1415). – trincot Feb 16 '22 at 07:27
2

Here is my implementation using Infinite Series

function calculatePI(iterations = 10000){
    let pi = 0;
    let iterator = sequence();

    for(let i = 0; i < iterations; i++){
        pi += 4 /  iterator.next().value;
        pi -= 4 / iterator.next().value;
    }

    function* sequence() {
      let i = 1;
      while(true){
        yield i;
        i += 2;
      }
    }

    return pi;
}
nikksan
  • 3,341
  • 3
  • 22
  • 27
-3

You can use this for your purpose

Math.PI.toFixed(n)

where n is the number of decimals you wish to display.

It displays the rounded value of pi. It can be considered fairly correct upto 15 decimal places.

Harigovind R
  • 816
  • 8
  • 17
  • 1
    the returned number is not a PI number. – Roko C. Buljan Jun 10 '15 at 05:01
  • 1
    This doesn't actually return pi, but the actual number for the closest approximation for pi as a double to n decimal places. – Qantas 94 Heavy Jun 10 '15 at 05:14
  • 1
    This doesn't actually [return](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) a [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), but a [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number). – ashleedawg May 06 '22 at 03:17
  • 3
    they are asking for pi that is **more** than 15 decimal places. Math.PI only has 15 decimal spaces, and so .toFixed() cannot magicly generate more digits of pi. – Hermanboxcar Jul 21 '22 at 17:05