-- RealFnsImpl.mesa -- Last modified: Stewart on August 27, 1982 1:07 pm DIRECTORY Ieee USING [CVExtended, ExponentBias, SingleReal, Unpack], Real USING [Extended, Fix, RealError, RealException], Inline USING [LowHalf], RealFns; RealFnsImpl: CEDAR PROGRAM IMPORTS Ieee, Inline, Real EXPORTS RealFns = BEGIN OPEN Ieee; Precision: CARDINAL ← 5; LoopCount: INTEGER ← 3; Ln2: REAL = 0.693147181; LogBase2ofE: REAL = 1/Ln2; --compute e↑n using continued fractions --See ACM Algorithm 229, Morelock, James C., "Elementary functions -- by continued fractions" Exp: PUBLIC PROC [x: REAL] RETURNS [REAL] = { r, f, kludge: REAL; k: LONG INTEGER; i, ikludge: INTEGER; IF x = 0 THEN RETURN[1]; x ← x*LogBase2ofE; --exponent of 2 (instead of e) k ← Real.Fix[x]; --integer part x ← x - k; --fractional part x ← x*Ln2; --now result = 2↑k * exp(x) and 0<=x<Ln2 r ← x*x; --f←4*LoopCount+2; i ← 4*LoopCount + 2; f ← i; FOR i DECREASING IN [1..LoopCount] DO ikludge ← 4*i - 2; kludge ← ikludge; f ← kludge + r/f; ENDLOOP; --unscaled result is (f+x)/(f-x) x ← (f + x)/(f - x); --scale: multipy by 2↑k by adding k to the exponent x ← Pow2[x, LOOPHOLE[Inline.LowHalf[LOOPHOLE[k]]]]; RETURN[x]; }; Pow2: PROC [x: REAL, exp: INTEGER] RETURNS [REAL] = INLINE { fl: SingleReal ← LOOPHOLE[1.0, SingleReal]; IF exp NOT IN [-126..127] THEN ERROR; fl.exp ← exp + ExponentBias; --scale: multipy by 2↑exp by adding exp to the exponent x ← x*LOOPHOLE[fl, REAL]; RETURN[x]; }; Log: PUBLIC PROC [base, arg: REAL] RETURNS [REAL] = { --logxy = Ln(y)/Ln(x) x, y: REAL; x ← Ln[base]; y ← Ln[arg]; RETURN[y/x]; }; Ln: PUBLIC PROC [x: REAL] RETURNS [REAL] = { fl: SingleReal; i: CARDINAL; m: INTEGER; kludge: REAL; OneMinusX, OneMinusXPowerRep, PreviousVal: REAL; IF x < 0 THEN { [] ← SIGNAL Real.RealException[flags: [invalidOperation: TRUE], vp: NEW[Real.Extended ← Ieee.CVExtended[Ieee.Unpack[x]]]]; ERROR Real.RealError[]; }; --if x = z * 2↑m then Ln(x) = m * Ln2 + Ln(z). Here, 1/2 <= z < 1 fl ← LOOPHOLE[x, SingleReal]; m ← fl.exp - (ExponentBias - 1); fl.exp ← ExponentBias - 1; --now 1/2 <= z < 1 x ← LOOPHOLE[fl, REAL]; --special case for limit condition (x a power of 2) IF x = 0.5 THEN {m ← m - 1; kludge ← m; x ← Ln2*kludge; RETURN[x]; }; OneMinusX ← 1 - x; x ← m*Ln2; OneMinusXPowerRep ← 1; FOR i IN [1..100] DO PreviousVal ← x; OneMinusXPowerRep ← OneMinusXPowerRep*OneMinusX; x ← x - OneMinusXPowerRep/i; IF x = PreviousVal THEN EXIT; ENDLOOP; RETURN[x]; }; SqRt: PUBLIC PROC [x: REAL] RETURNS [REAL] = { epsilon: REAL = Pow2[1, -23]; y, x1: REAL; fl: SingleReal; IF x <= 0 THEN RETURN[0]; fl ← LOOPHOLE[x, SingleReal]; fl.exp ← (fl.exp - ExponentBias)/2 + ExponentBias; y ← LOOPHOLE[fl, REAL]; --initial guess = half original exponent DO x1 ← y*y; y ← y - (x1 - x)/(2*y); --slope of the curve here is 1/2 IF ABS[(x1 - x)/x] <= epsilon THEN EXIT; -- may need ABS[] ENDLOOP; RETURN[y]; }; Root: PUBLIC PROC [index, arg: REAL] RETURNS [REAL] = { --yth root of x = e↑(Ln(x)/y) RETURN[Exp[Ln[arg]/index]]; }; Power: PUBLIC PROC [base, exponent: REAL] RETURNS [REAL] = { --x↑y = e↑(y*Ln(x)) x: REAL; IF base = 0 THEN IF exponent = 0 THEN RETURN[1] ELSE RETURN[0]; x ← Ln[base]; RETURN[Exp[exponent*x]]; }; Cos: PUBLIC PROC [radians: REAL] RETURNS [cos: REAL] = { -- This function is good to 7.33 decimal places and is taken from: -- Computer Approximations, John F. Hart et.al. pp118(top 2nd col). negsign: REAL ← 1; nangle: REAL ← ABS[radians - Real.Fix[radians*recpi]*twoPI]; SELECT nangle FROM IN [PIovr2..PI3ovr2) => BEGIN negsign ← -1; nangle ← ABS[PI - nangle]; END; IN [PI3ovr2..twoPI) => nangle ← twoPI - nangle; ENDCASE; nangle ← nangle*nangle; cos ← negsign*(cp0 + nangle*(cp1 + nangle*(cp2 + nangle*(cp3 + nangle*cp4)))); }; CosDeg: PUBLIC PROC [degrees: REAL] RETURNS [cos: REAL] = { radians: REAL ← degrees*degtorad; cos ← Cos[radians]; }; Sin: PUBLIC PROC [radians: REAL] RETURNS [sin: REAL] = { radians ← PIovr2 - radians; sin ← Cos[radians]; }; SinDeg: PUBLIC PROC [degrees: REAL] RETURNS [sin: REAL] = { radians: REAL ← degrees*degtorad; sin ← Sin[radians]; }; Tan: PUBLIC PROC [radians: REAL] RETURNS [tan: REAL] = { tan ← Sin[radians]/Cos[radians]; }; TanDeg: PUBLIC PROC [degrees: REAL] RETURNS [tan: REAL] = { radians: REAL ← degrees*degtorad; tan ← Sin[radians]/Cos[radians]; }; ArcTan: PUBLIC PROC [y, x: REAL] RETURNS [radians: REAL] = { -- This function is good to 8.7 decimal places and is taken from: -- Computer Approximations, John F. Hart et.al. pp129(top 2nd col). s, v, t, t2, c, q: REAL; IF ABS[x] <= ABS[smallnumber] THEN IF ABS[y] <= ABS[smallnumber] THEN RETURN[0] ELSE IF y < 0 THEN RETURN[-PIovr2] ELSE RETURN[PIovr2]; IF x < 0 THEN IF y < 0 THEN {q ← -PI; s ← 1; } ELSE {q ← PI; s ← -1; } ELSE IF y < 0 THEN {q ← 0; s ← -1; } ELSE {q ← 0; s ← 1; }; v ← ABS[y/x]; SELECT v FROM IN [0..tanPI16) => {t ← v; c ← 0; }; IN [tanPI16..tan3PI16) => {t ← (x2) - ((x22)/(x2 + v)); c ← PIovr8; }; IN [tan3PI16..tan5PI16) => {t ← (x4) - ((x44)/(x4 + v)); c ← PIovr4; }; IN [tan5PI16..tan7PI16) => {t ← (x6) - ((x66)/(x6 + v)); c ← PI3ovr8; }; >= tan7PI16 => {t ← -1/v; c ← PIovr2; }; ENDCASE; t2 ← t*t; radians ← s*(t*(p0 + t2*(p1 + t2*(p2 + t2*p3))) + c) + q; }; ArcTanDeg: PUBLIC PROC [y, x: REAL] RETURNS [degrees: REAL] = { radians: REAL ← ArcTan[y, x]; degrees ← radians*radtodeg; }; AlmostZero: PUBLIC PROC [x: REAL, distance: INTEGER [-126..127]] RETURNS [BOOLEAN] = { fl: Ieee.SingleReal ← LOOPHOLE[x]; RETURN[fl.exp < (distance + Ieee.ExponentBias)]; }; AlmostEqual: PUBLIC PROC [y, x: REAL, distance: INTEGER [-126..0]] RETURNS [BOOLEAN] = { fl: Ieee.SingleReal ← LOOPHOLE[(x - y)]; oldexp: INTEGER ← MAX[ LOOPHOLE[x, Ieee.SingleReal].exp, LOOPHOLE[y, Ieee.SingleReal].exp]; RETURN[fl.exp < (distance + oldexp)]; }; smallnumber: REAL = 0.00000000005; PI: REAL = 3.14159265; twoPI: REAL = PI*2; PI3ovr2: REAL = 3*PI/2; PIovr2: REAL = 1.570796327; PI3ovr8: REAL = 1.1780972; PIovr4: REAL = .785398163; PIovr8: REAL = .392699; cp0: REAL = .999999953; cp1: REAL = -.499999053; cp2: REAL = .0416635847; cp3: REAL = -.001385370426; cp4: REAL = .0000231539317; radtodeg: REAL = 180/PI; degtorad: REAL = PI/180; recpi: REAL = 1/twoPI; tanPI16: REAL = .1989123673; x2: REAL = 1/(.414213562); x22: REAL = x2*x2 + 1; tan3PI16: REAL = .668178638; x4: REAL = 1/(1.0); x44: REAL = x4*x4 + 1; tan5PI16: REAL = 1.496605763; x6: REAL = 1/(2.41421356); x66: REAL = x6*x6 + 1; tan7PI16: REAL = 5.02733949; p0: REAL = .999999998; p1: REAL = -.333331726; p2: REAL = .1997952738; p3: REAL = -.134450639; END. September 16, 1980 3:13 PM, Stewart; Bug in Exp September 28, 1980 8:28 PM, Stewart; Add AlmostEqual and AlmostZero, format November 7, 1980 3:33 PM, Stewart; fix AlmostZero August 27, 1982 1:07 pm, Stewart; CEDAR