More templated version

Craig Black cblack at ara.com
Sat Apr 29 07:24:14 PDT 2006


Here' a version that uses more templates.  It performs the same as the first
version I submitted.

import std.c.stdio;
import std.c.stdlib;
import std.c.time;

const int LONG_TIME=4000;

byte[] p;
byte[] t;
int q;

int main(char[][] args)
{
 if (args.length == 2) {
  sscanf(&args[1][0],"%d",&q);
 } else {
  printf("Usage: pi [precision]\n");
  exit(55);
 }

 if (q < 0)
 {
  printf("Precision was too low, running with precision of 0.\n");
  q = 0;
 }

 if (q > LONG_TIME)
 {
     printf("Be prepared to wait a while...\n");
 }

 // Compute one more digit than we display to compensate for rounding
 q++;

 p.length = q + 1;
 t.length = q + 1;

 // Compute pi
 time_t startime, endtime;
 std.c.time.time(&startime);
 arctan!(2);
 arctan!(3);
 fastmul!(4);
 std.c.time.time(&endtime);

 // Return to the number of digits we want to display
 q--;

 // Print pi
 printf("pi = %d.",cast(int)(p[0]));
 for (int i = 1; i <= q; i++)
  printf("%d",cast(int)(p[i]));
 printf("\n");
 printf("%ld seconds to compute pi with a precision of %d
digits.\n",endtime-startime,q);

 return 0;
}

/* Template that determines if a number is a power of
 * 2 at compile time
 */
template isPow2(uint n) { const bool isPow2 = (n % 2) != 0 ? false :
isPow2!(n / 2); }
template isPow2(uint n : 0) { const bool isPow2 = false; }
template isPow2(uint n : 1) { const bool isPow2 = false; }
template isPow2(uint n : 2) { const bool isPow2 = true; }

/* Use a template to get good compile time optimizations for
 * division and multiplication
 */
template fastdiv(int divisor)
{
 void fastdiv()
 {
  int r; // remainder


  for (int i = 0; i <= q; i++) {
   int b = t[i] + r * 10;
   int q = b / divisor;
   static if(isPow2!(divisor))
    r = b % divisor; // compiler can optimize modulus for powers of 2
   else
    r = b - q * divisor;
   t[i] = q;
  }
 }
}

/* Template to compute the arctangent.
 * The constant input parameter allows the compiler to optimize.
 */
template arctan(int s)
{
 void arctan()
 {
  int n;

  t[0] = 1;
  fastdiv!(s);
  add();
  n = 1;
  do {
   mul(n);
   fastdiv!(s*s);
   div(n += 2);
   if ((((n-1) / 2) % 2) == 0)
    add();
   else
    sub();
  } while (!tiszero());
 }
}

void add()
{
 for (int j = q; j >= 0; j--)
 {
  if (t[j] + p[j] > 9) {
   p[j] += t[j] - 10;
   p[j-1]++;
  } else
   p[j] += t[j];
 }
}

void sub()
{
 for (int j = q; j >= 0; j--)
  if (p[j] < t[j]) {
   p[j] -= t[j] - 10;
   p[j-1]--;
  } else
   p[j] -= t[j];
}


void mul(int multiplier)
{
 int c;

 for (int i = q; i >= 0; i--) {
  int b = t[i] * multiplier + c;
  c = b / 10;
  t[i] = b - c * 10;
 }
}

/* Fast multiplication using template */
template fastmul(int multiplier)
{
 void fastmul()
 {
  int c;

  for (int i = q; i >= 0; i--) {
   int b = p[i] * multiplier + c;
   c = b / 10;
   p[i] = b - c * 10;
  }
 }
}


void div(int divisor)
{
 int r; // remainder

        /* Optimization: It's faster to do floatint point multiplication
than
         * integer division. This is because there is no integer division
instruction.
         * Under the hood, integer division is essentially floating-point
division.
         */
        double idiv = 1.0 / divisor;

 for (int i = 0; i <= q; i++) {
                int b = t[i] + r * 10;
  int quotient = cast(int)(cast(double)b * idiv);
  r = b - quotient * divisor;
  t[i] = quotient;
 }
}

int tiszero()
{
 for (int k = 0; k <= q; k++)
  if (t[k] != 0)
   return false;
 return true;
}






More information about the Digitalmars-d mailing list