In ECMAscript/Javascript, we already have π to a nice precision:
ECMAscript (つまり、Javascript)には、まあまあの正確性までに π を出すことができます。

And we can extend it arbitrarily,
好きなだけ桁数を伸ばすことができます。

but, woops! Those extra digits are not π. (Not really a surprise.) Here's what they should be:
ああっ!しまった。増やした桁数は π ではない。(ビックリもしない。)本当の π の30桁まではこういうのです。

3.14159265358979323846264338328

In the Unix/Linux utility, bc, or in Java, we can calculate π to an arbitrary number of digits. Here's the bc source for a slow method, basically alternately adding and subtracting the reciprocals of the odd numbers:

  4 / 1 = 4
- 4 / 3 = 2.66666666666666...
+ 4 / 5 = 3.46666666666666...
- 4 / 7 = 2.89523809523809...
+ 4 / 9 = 3.33968253968254...
- 4 / 11 = 2.97604617604617...
...

My quick bc source code:

# Madhava-Leibniz-Gregory
define makepie( limit )
{
  sum = 0;
  direction = 1;
  for ( odd = 1; odd < limit; odd += 2 ) 
  {
    if ( direction > 0 ) sum += 4/odd else sum -= 4/odd ;
    direction = -direction;
    print sum; print ": "; print odd; print "\n"
  }
  return sum;
}

A much faster way in bc, avioding using the mathlib option by using a brute-force power function:

define power( x, y )
{
  pow = 1;
  for ( i = 0; i < y; ++i )
  {
    pow *= x;
  }
  return pow;
}

# Madhava
define makepie2( limit )
{
  sum = 0;
  for ( k=0; k < limit; ++k )
  {
    sum += 1 / ( power( -3, k ) * ( 2 * k + 1 ) );
    print sum; print ": "; print k; print "\n"   
  }
  return sum * sqrt(12);
}

The same, but, just for fun, using a brute-force square root function (and the above brute-force power function):

define squareroot( n ) 
{
  oldguess = 0;
  guess = 1;
  result = n;
  while ( ( guess != result ) && ( guess != oldguess ) )
  {
    oldguess = guess;
    guess = ( guess + result ) / 2;
    result = n / guess;
    print guess, ": ", result, "\n" ;
  }
  return result;
}

# Madhava, with our own square root (less accurate).
define makepie3( limit )
{
  sum = 0;
  for ( k=0; k < limit; ++k )
  {
    sum += 1 / ( power( -3, k ) * ( 2 * k + 1 ) );
    print sum; print ": "; print k; print "\n"   
  }
  return sum * squareroot(12);
}

Similar stuff in Java:

// A few routines to simulate circular functions in java's BigDecimal.
// Doing this properly, I should extend BigDecimal instead of making the methods static. 
// It requires too much setup that I'm not really used to. 
// Maybe later.


import java.math.*;

public class Pi
{
	public static void main( String[] arguments )
	{
		int flag = 0;
		int method = 
			( ( arguments.length > flag ) && arguments[ flag++ ].startsWith( "-slow" ) )
			? 1 : 2;
		int precision = ( method == 1 ) ? 20 : 50;
		int limit = ( method == 1 ) ? 100000 : 110;
		if ( arguments.length > flag )
		{	limit = Integer.valueOf( arguments[ flag++ ] );
		}
		if ( arguments.length > flag )
		{	precision = Integer.valueOf( arguments[ flag++ ] );
		}
		BigDecimal pi = BigDecimal.valueOf( -999 );
		if ( method == 1 )
		{
			pi = Pi.slow( limit, precision );
			System.out.print( "\nslow: " + pi.toString() + "\n" );
		}
		else
		{
			pi = Pi.faster( limit, precision );
			System.out.print( "\nfaster: " + pi.toString() + "\n" );
		}
	}

	// Madhava-Leibniz-Gregory
	public static BigDecimal slow( int limit, int precision )	
	{
		BigDecimal sum = BigDecimal.valueOf( 0.0 );	// "scale" is scale of exponent, not precision.
		System.out.println( "slow start: " + sum.toString() );
		int direction = 1;
		for ( int odd = 1; odd < limit; odd += 2 ) 
		{
			BigDecimal term 
				= ( BigDecimal.valueOf( 4.0 ) ).divide( 
						BigDecimal.valueOf( odd ), precision /* erk! */, 
							BigDecimal.ROUND_HALF_EVEN );
			System.out.print( "this term " + term.toString() + ": " );
			if ( direction > 0 ) 
				sum = sum.add( term ); 
			else 
				sum = sum.subtract( term );
			direction = -direction;
			System.out.println( "slow: " + sum.toString() + ": " + odd );
		}
		return sum;
	}

	// These utility functions are okay here, not generally useful.
	private static BigDecimal power( BigDecimal x, int y )
	{
		BigDecimal pow = BigDecimal.valueOf( 1.0 );
		System.out.println( "power (" + x.toString() + ")^" + y + " start: " + pow.toString() );
		for ( int i = 0; i < y; ++i )
		{
			pow = pow.multiply( x );
		}
		System.out.println( "returning power: " + pow.toString() );
		return pow;
	}

	private static BigDecimal squareRoot( BigDecimal n, int precision )
	{
		BigDecimal oldguess = BigDecimal.valueOf( 0.0 );
		BigDecimal guess = BigDecimal.valueOf( 1.0 );
		BigDecimal result = n;
		result.setScale( precision, BigDecimal.ROUND_HALF_EVEN );
		while ( ( guess.compareTo( result ) != 0 ) 
				 && ( guess.compareTo( oldguess ) != 0 ) 
				)
		{
			oldguess = guess;
			guess = guess.add( result )
						.divide( 
							BigDecimal.valueOf( 2.0 ), 
							precision + 2, BigDecimal.ROUND_HALF_EVEN );
			result = n.divide( 
							guess, 
							precision + 2, BigDecimal.ROUND_HALF_EVEN );
			System.out.println( "root: " + guess.toString() + ": " + result.toString() );
		}
		return result;
	}

	// Madhava and others
	public static BigDecimal faster( int limit, int precision )	
	{
		BigDecimal sum = BigDecimal.valueOf( 0.0 );
		sum.setScale( precision + 2, BigDecimal.ROUND_HALF_EVEN );
		System.out.println( "faster limit: " + limit + ", precision: " + precision + " sum: " + sum.toString() );
		for ( int k = 0; k < limit; ++k )
		{
			BigDecimal powerTerm = power( BigDecimal.valueOf( -3.0 ), k );
			int productTerm = 2 * k + 1;
			BigDecimal denominator = powerTerm.multiply( BigDecimal.valueOf( productTerm ) );
			BigDecimal partial = BigDecimal.valueOf( 1.0 )
							.divide( denominator, precision + 2, BigDecimal.ROUND_HALF_EVEN );
			sum = sum.add( partial );
			System.out.println( "faster loop: " + sum.toString() + ": " + k );
		}
		return sum.multiply( squareRoot( BigDecimal.valueOf( 12.0 ), precision + 2 ).setScale( precision, BigDecimal.ROUND_HALF_EVEN ) );
		// That setScale doesn't seem to do what I think it should.
	}
}

And, oh, hey, here's the easy way to do it in bc. It does require the mathlib option:

[user@workstation ~]$ bc -l

... then use the arctangent function. Don't forget to set the scale to a bit more than the number of digits that you want:

4 * a( 1 )
3.14159265358979323844
scale = 50
4 * a( 1 )
3.14159265358979323846264338327950288419716939937508

Too bad Java doesn't have a BigDecimal version of arctangent. (Well, there are places you can get such. Or you can build your own, something like I'm showing with π. Look up "calculating pi" on wikipedia.)


So, can we do something here for Javascript? -- similar to what we can do in bc and Java?

Let's give it a try with .toPrecision. Check the source for this page for one way to do it. Results below:

Well, so much for that idea. The .toPrecision() method is apparently only for output, and can't be used to extend precision.

There would be a way to do it with arrays and objects, but we would basically have to construct arbitrary precision numbers (like Java's BigDecimal) ourselves, then define the math functions for them. But, just for fun, let's see what happens with the precision we have, the fast way with a limit of 40 and the slow way with a limit of 10000, outputting five digits more precision than we have:

Copyright 2011 Joel Rees