% file pi.tex version 0.993 % % **** Compute Pi in TeX! **** % % % Author: D. Roegel (roegel@loria.fr) % % Version 0.96: 22 July 1996 % First release % % Version 0.97: 22 July 1996 % Modified by J. Gelinas (jacquesg@clic.net.ca) % Added one term to get correct last digits % Added a second optional argument: % 100 prints decimals 0..100 % -2 100 prints only decimals 100..101 % Print exact number of decimals % % Version 0.98: 23 July 1996 % Modified by D. Roegel, so that the input -3 1 gives 141 % and not 3.141. Also, some unnecessary braces inside \loop...\repeat % were removed. \ShowResult shortened by introduction of \NextDigit. % % Version 0.99: 23 July 1996 % Modified by D. Roegel. % Improvements following suggestions by J. Gelinas (jacquesg@clic.net.ca). % Bug corrected, which made some digits false (\fontdimen\firstpos\xb=0pt % added in \updatefirstpos). % \N replaced by \count2 in \ShowResult, this enabling calls % such as \ShowResult{1/\the\N...}{...}. % % Version 0.991: 23 July 1996 % Modified by D. Roegel. % Two \UpdateFirstPos were removed. % % Version 0.992: 24 July 1996 % Modified by D. Roegel, following several simplifications suggested % by J. Gelinas (jacquesg@clic.net.ca). % \Multiply removed, \Add and \Sub merged. % \ComputeArcTan shortened. % % Version 0.993: 24 July 1996 % Modified by D. Roegel, following several simplifications suggested % by J. Gelinas (jacquesg@clic.net.ca). % \ComputeArcTan1/n computes now completely arctan(1/n). % %------------------------------------------------------------------------ % This programs uses the formula by John Machin: % % Pi=16*arctan(1/5)-4*arctan(1/239) % % For arctan(x), we use the development % % arctan(x)=\sum_{i=0}^\infty [{x^{2i+1}\over 2i+1} - {x^{2i+3}\over 2i+3}] % % One array (\xc) is used to store the partial sum up to {x^{2i+1}\over 2i+1}. % A second array (\xa) us used to store the value of x^{2i+1}. % A third array (\xb) is used to store {x^{2i+1}\over 2i+1}. % % The result is put in the array \xr. % % The implementation of arrays uses a trick shown by Tom Rokicki % in his game of life program (life.tex). % % The number of digits you can compute depends on your implementation % of TeX. I had no trouble computing 5000 digits. % % The last digit can be wrong. And if it is a 0 or a 9, this decimal % and previous ones can be wrong too. However, the absolute error is % no greater than one unit of the last digit. % % If you want to know more about Pi, check the file sci.math.faq. % See also http://www.primus.com/staff/paulp/useless/pi.html % and http://www.tu-chemnitz.de/~arndt/joerg.html. % %------------------------------------------------------------------------ % \newlinechar=`\^^J \message{^^J***** Computation of Pi with John Machin's formula *****} \message{^^J** i.e.: pi=16*arctan(1/5)-4*arctan(1/239)} \message{^^JHow many decimals of pi do you want ? } \read16to\nbdigits \newcount\n \n\nbdigits \ifnum\n<0 \multiply\n-1 \message{^^JFirst decimal to output ? } \read16to\firstdigit \else \advance\n1 \def\firstdigit{0} \fi \newcount\lastdigit \lastdigit\firstdigit \advance\lastdigit\n \advance\lastdigit-1 \newcount\index \index\lastdigit \advance\index11 \divide\index4 \def\base{10000} \def\basesp{10000sp} \newcount\lastplusone \lastplusone\index \advance\lastplusone1 % \index is now the index for the last slot in the arrays % slot 1 -> integer digits % slot 2 -> digits 1 to 4 % slot 3 -> digits 5 to 8 % ... % slot \index -> digits (\index-2) * 4 +1 to (\index-1) * 4 % \font\xa=cmr10 at 11truept % array for current values of (1/5)^{2n+1} % and (1/239)^{2n+1} \fontdimen\lastplusone\xa=0sp % this creates room \font\xb=cmr10 at 13truept % array for current values of (1/5)^{2n+1}/(2n+1) % and (1/239)^{2n+1}/(2n+1) \fontdimen\lastplusone\xb=0sp % this creates room \font\xc=cmr10 at 15truept % array for current sums of arctan(1/5) % and arctan(1/239) \fontdimen\lastplusone\xc=0sp % this creates room \font\xr=cmr10 at 17truept % array for the result \fontdimen\lastplusone\xr=0sp % this creates room % (we have each time allocated one more slot than strictly necessary; % this avoids a test on \lastplusone in \updatefirstpos) % \xa, \xb, \xc and \xr are now equal to 0 % Some variables (some of them are not strictly necessary, and might be % replaced by \count's): \newcount\dv % will hold dividers \newcount\firstpos % first non empty slot \newcount\I % scratch register for loops \newdimen\carry % for carry (in additions) and borrows (in subtractions) \newdimen\x % a scratch variable \newcount\N % counts the terms \newif\ifcont % flag used to find when an operation on bignums is not done \newcount\Sdv % value of one digit (used in \ShowResult) \newcount\dir % toggle for alternating sums % Initialization of working arrays \def\InitializeArrays{ { \I=1 \loop \fontdimen\I\xa=0sp \fontdimen\I\xb=0sp \fontdimen\I\xc=0sp \advance\I1 \ifnum\I<\lastplusone \repeat } } % Initialization of the result \newcount\I \I=1 \loop \fontdimen\I\xr=0sp \advance\I1 \ifnum\I<\lastplusone \repeat % divide array #1 by #2 beginning at slot \firstpos and up to \index; % result is in array #3 % Maximum carry is 9999, so we need to be able to store 99999999. % \dimen's can hold up to +/- 2,147,483,647 sp and % \count's up to +/- 2,147,483,647, so it fits. \def\Divide#1#2into#3{% \carry0sp \I\firstpos { \loop \x=\fontdimen\I#1 \multiply\carry\base \advance\x\carry \carry\x \divide\x#2 \fontdimen\I#3=\x \multiply\x#2 \advance\carry-\x \advance\I1 \ifnum\I<\lastplusone \repeat } } % Add or Subtract #2 to #3, depending on #1. array #2 is not modified. \def\Add#1#2to#3{% \carry0sp \I\index { \loop \x=\fontdimen\I#3 \advance\x by #1\fontdimen\I#2 \advance\x by #1\carry \fontdimen\I#3=\x \carry\x \divide\carry\base \multiply\carry\base \advance\x-\carry \divide\carry\base \ifdim\x<0sp \advance\x\basesp \advance\carry1sp \fi \fontdimen\I#3=\x \advance\I-1 \ifnum\I<\firstpos \ifnum\carry=0 \contfalse \else \conttrue \fi \else \conttrue \fi \ifcont \repeat } } % Multiply array #1 by #2. Result is in #1. % This macro is not used in this program, and only remains % here for didactic and historical reasons. \def\Multiply#1#2{% \carry0sp \I\index \loop \x=\fontdimen\I#1 \multiply\x by #2 \advance\x by \carry \fontdimen\I#1=\x \carry\x \divide\carry\base \multiply\carry\base \advance\x-\carry \fontdimen\I#1=\x \divide\carry\base \advance\I-1 \ifnum\I<\firstpos \ifnum\carry=0 \contfalse \else \conttrue \fi \else \conttrue \fi \ifcont \repeat } % update value of \firstpos; in the worst case, \firstpos % gets increased by 2. \def\updatefirstpos{ \ifdim\fontdimen\firstpos\xa=0sp \ifnum\firstpos<\lastplusone \fontdimen\firstpos\xb=0sp \advance\firstpos1 \fi \fi } \def\UpdateFirstPos{\updatefirstpos\updatefirstpos} % Compute arctan(1/#1). Arrays \xa and \xb are used. Result is in \xc. \def\ComputeArcTan1/#1{% \firstpos1 \InitializeArrays % initialize \xa with 1: \fontdimen1\xa=1sp \Divide\xa{#1}into\xa % now, \xa contains 1/#1 \Add1\xa to\xb % \xb contains 1/#1 \firstpos=2 \dv=#1 \multiply\dv\dv \N1 \dir-1 \message{^^JI am now computing the following sum: arctan(1/#1)=1/#1} \Add1\xb to\xc % first term \loop \Divide\xa\dv into\xa \UpdateFirstPos \ifnum\firstpos<\lastplusone \advance\N2 \Divide\xa\N into\xb \ifnum\dir>0 \message{+1/\the\N*1/#1^\the\N} \else \message{-1/\the\N*1/#1^\the\N} \fi \Add\dir\xb to\xc \multiply\dir-1 \repeat } % Extract the next digit from \count1 \def\NextDigit#1{ \ifnum\count2<\lastdigit \Sdv\count1 \divide\Sdv#1 \advance\count2 1 \ifnum\count2>\firstdigit \edef\res{\res\number\Sdv} \fi \multiply\Sdv#1 \advance\count1-\Sdv \fi } % Display the result; the digits are scanned one at a time, % and those between \firstdigit and \lastdigit are extracted % and saved in \res. \def\ShowResult#1#2{{ \count0=2 \count2=1 \advance\lastdigit1 \def\res{} \loop \count1=\fontdimen\count0#2 \NextDigit{1000} \NextDigit{100} \NextDigit{10} \NextDigit{1} \advance\count0by 1 \ifnum\count2<\lastdigit \repeat \advance\lastdigit-1 % for correct display in next line \message{^^J#1\res} } } \ComputeArcTan1/5 %\ShowResult{arctan(1/5)=0.}\xc \message{^^JI multiply it by 4...} \firstpos2 \Add1\xc to\xc % alternative method is to write \Multiply\xc4 % or to change \ComputeArcTan so that the initial value is 4 \Add1\xc to\xc \message{done} % add 4*arctan(1/5) to \xr \Add1\xc to\xr %\ShowResult{4*arctan(1/5)=0.}\xr \ComputeArcTan1/{239} %\ShowResult{arctan(1/239)=0.}\xc %\ShowResult{4*arctan(1/239)=0.}\xc \message{^^JI subtract arctan(1/239) to 4*arctan(1/5)...} \firstpos2 \Add{-1}\xc to\xr \message{done} \message{^^JAnd finally, I multiply it by 4 giving:} \Add1\xr to\xr \Add1\xr to\xr \ifnum\firstdigit=0 \ShowResult{pi[0 .. \number\lastdigit]=3.}\xr \else \ShowResult{pi[\firstdigit.. \number\lastdigit]=}\xr \fi \bye