pi.i is easily the most twisted and obscure INTERCAL program I've yet
written. I say that because even I don't fully understand why it
works. The program writes in one number from the input, then prints
out that many digits of pi. There are no fancy INTERCAL tricks here;
all it does is simple integer arithmetic.
The algorithm used comes from an old TECO macro that was recently
discussed on alt.lang.teco. Mark Henderson posted a translation
into C, which at least made it easier to read, if not much easier to
understand. I played around with that program a bit, fixed a couple
of minor bugs which may or may not have existed in the original TECO,
and modified it a bit to put in into a form that would be easy to
translate into INTERCAL. That modified program, which corresponds
closely to the algorithm in pi.i, is reproduced at the end of this
file.
The INTERCAL version has only been tested up to a hundred digits,
which took almost 30 minutes on a Sparc 1. The C version below,
with variables declared unsigned short to correspond to the INTERCAL,
gives up to 535 accurate digits and then starts printing garbage,
presumably because of an overflow. If the declarations are changed
to short only the first 262 digits are accurate, while using int
yields thousands of accurate digits. Though I don't understand
what the inner loop is doing, it looks like the largest numbers
encountered are O(n), where n is the number of digits requested.
If, as I expect, the present INTERCAL version does fail after about
535 digits, it could easily be converted to a 32-bit form which
would run as long as almost anyone would desire.
The only feature likely to be of utility in other INTERCAL programs
is the new (2030) routine, which performs a 16-bit division with
remainder. It is faster than the (1040) routine for two reasons:
First, it is a true 16-bit version, whereas the (1040) routine just
shifts over its operands and then calls the 32-bit division routine
(1550). Second, the (1550) routine generates a remainder as part of
the computation, but then simply throws it away. I needed the
remainder, so I have my (2030) routine return it in .4 and the
quotient in .3. In other respects this is just a 16-bit translation
of the (1550) routine.
Louis Howell
December 30, 1991
#include
unsigned short line[10000];
main(argc,argv)
int argc;
char *argv[];
{
unsigned short n, a, i, q, v, dummy, h=0, w=0;
if (argc==2)
n=atoi(argv[1]);
else
n=20;
n+=2;
for (i=(n*10)/3 ; i > 0 ; i--) {
line[i-1] = 2;
}
for (dummy=0; dummy < n; dummy++) {
q=0;
for (i=(n*10)/3 ; i > 0 ; i--) {
a = line[i-1] * 10 + (q*i);
q=a/(i*2-1);
line[i-1] = a%(i*2-1);
}
v = w;
w = h+(q/10);
h = q%10;
if (w==10) {
w=0;
v++;
}
if (dummy!=0)
printf("%d", v);
}
printf("\n");
}