MODULE EXPONENT; { Copyright (c) by T. W. Lougheed 24 April 1981 } { First version 8 March 1981 By T. W. Lougheed Dept. T. & A. Mechanics Thurston Hall, Cornell U. Ithaca, NY 14853 Last version 27 April 1981 This software is in the public domain, and may not be sold by any person or corperation without permission of the author. } FUNCTION EXP( Z :REAL) :REAL; CONST E = 2.718281828459045; { Base of the natural logarithms. } MAXREAL = 9.9999999E17; { Result of overflow. } MINREAL = 1.0E-17; { Result of an underflow. Could be set to be zero just as resonably. } CAUSES_OVERFLOW = 41.4465; { Any argument greater in absolute value than this will cause overflow. } VAR Y :REAL; N :INTEGER; { Intermediate values. } { The following funtion returns e raised to an INTEGER power N. The sign of N is ignored: its absolute value is used. } FUNCTION POWER( R :REAL; N :INTEGER ) :REAL; VAR X, F : REAL; M : INTEGER; { This algorithm uses the 'invariant' method. The following equasion is always true, it is the 'invariant': n m R = Y = X F "n" is the power we want to raise "R" to, "Y" is the answer we are seeking, "X" is on its way to being the answer (starts at 1) and "m" starts at "n" and arrives at "0" when "X" has arrived at the answer "Y". "F" initially starts at "R" and increases as necessary to keep the invariant formula true. Since M = 2 (M div 2) + M mod 2, if we replace X with X * F**(M mod 2) and then F with F**2 and M with M div 2, the invariant formula is unchanged. So we itterate this set of replacements until we see that M is zero. As mentioned before, M is zero, X must be the desired answer "Y". The only fact left to note is that M mod 2 = M bit 0, and M div 2 = M shr 1, so we can do all that jazz cheaply. } BEGIN F := R; X := 1; M := ABS( N ); IF M <> 0 THEN REPEAT IF TSTBIT(M, 0) THEN X := X*F; M := SHR(M, 1); F := SQR(F); UNTIL M = 0; POWER := X; END; { Note that the description is much longer than the algorithm: a hallmark of the method of programming with 'invariants'. } { Note also that the method only works well if you're clever at guessing what a good invariant could be. } { The following function gives a Tchebyshev polinomial approximation for EXP(X) for 0 < X < 0.69; the error without truncation would be less than 2E-10. Adapted from quasion 4.2.45 from THE HANDBOOK OF MATHEMATICAL FUNCTIONS, by Abramowicz and Stegun. This is really SUPPOSED to have negative arguments ... to give 1/EXP ... but I'm using it differently. } FUNCTION TCHEBYSHEV( X :REAL ) :REAL; CONST A1 = 0.9999999995; A2 = 0.4999999206; A3 = 0.1666653019; A4 = 0.0416573475; A5 = 0.0083013598; A6 = 0.0013298820; A7 = 0.0001413161; BEGIN TCHEBYSHEV := ((((((A7*X + A6)*X + A5)*X + A4)*X + A3)*X + A2)*X + A1)*X + 1; END; BEGIN { Main } IF Z > CAUSES_OVERFLOW THEN EXP := MAXREAL { These are special cases that we would like to handle exactly } ELSE IF Z = 1 THEN EXP := E ELSE IF Z = 0 THEN EXP := 1 ELSE IF Z = -1 THEN EXP := 1/E ELSE IF Z > 0 THEN BEGIN { Split up Z into its nearest integer part, N, and the remainder, Z. Let POWER handle the integer part and have TCHEBYSHEV handle the fractional part. } N := ROUND( Z ); Z := Z - N; Y := POWER( E, N ); EXP := Y*TCHEBYSHEV( Z ); END ELSE IF Z > -CAUSES_OVERFLOW THEN BEGIN N := ROUND( - Z ); Z := Z + N; Y := POWER( E, N ); EXP := Y/TCHEBYSHEV(Z); END ELSE EXP := MINREAL; { Underflow. } END; MODEND .