DIRECTORY IeeeInternal USING [CVExtended, ExponentBias, SingleReal, Unpack], Real USING [Extended, Fix, FixC, FixI, RealError, RealException], RealFns; RealFnsImpl: CEDAR PROGRAM IMPORTS IeeeInternal, Real EXPORTS RealFns = BEGIN Ln2: REAL = 0.693147181; LogBase2ofE: REAL _ 1/Ln2; 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; radiansPerDegree: REAL _ PI/180; degreesPerRadian: REAL _ 180/PI; rec2pi: REAL _ 1.0/twoPI; rec360: REAL _ 1.0/360.0; fourOverPI: REAL _ 4.0/PI; LoopCount: NAT = 3; Exp: PUBLIC PROC [x: REAL] RETURNS [REAL] = { r, f: REAL; k: INTEGER; fl: IeeeInternal.SingleReal _ LOOPHOLE[1.0, IeeeInternal.SingleReal]; IF x = 0 THEN RETURN[1]; x _ x*LogBase2ofE; --exponent of 2 (instead of e) IF x < -IeeeInternal.ExponentBias THEN RETURN [0.0]; IF x > IeeeInternal.ExponentBias THEN ERROR Real.RealError[]; k _ Real.FixI[x]; --integer part x _ x - k; --fractional part x _ x*Ln2; --now result = 2^k * exp(x) and 0<=x 0.0 THEN { aFmt: SquareReal _ LOOPHOLE[a, SquareReal]; bFmt: IeeeInternal.SingleReal _ LOOPHOLE[sqGuesses[aFmt.index], IeeeInternal.SingleReal]; bFmt.exp _ bFmt.exp + aFmt.expD2 - 63; b _ LOOPHOLE[bFmt, REAL]; b _ LOOPHOLE[LOOPHOLE[b + a/b, LONG CARDINAL] - 40000000B, REAL]; b _ LOOPHOLE[LOOPHOLE[b + a/b, LONG CARDINAL] - 40000000B, REAL]; }; }; sqGuesses: ARRAY[0..32) OF REAL _ [ LOOPHOLE[7715741440B, REAL], -- 0.7178211 LOOPHOLE[7717240700B, REAL], LOOPHOLE[7720514140B, REAL], LOOPHOLE[7721745140B, REAL], LOOPHOLE[7723155200B, REAL], LOOPHOLE[7724346000B, REAL], LOOPHOLE[7725517500B, REAL], LOOPHOLE[7726654000B, REAL], -- 0.8568115 LOOPHOLE[7727773400B, REAL], -- 0.8748627 LOOPHOLE[7731077100B, REAL], LOOPHOLE[7732167300B, REAL], LOOPHOLE[7733245000B, REAL], LOOPHOLE[7734310400B, REAL], LOOPHOLE[7735342500B, REAL], LOOPHOLE[7736363400B, REAL], LOOPHOLE[7737373700B, REAL], -- 0.9920616 LOOPHOLE[7740370240B, REAL], -- 1.015156 LOOPHOLE[7741351440B, REAL], LOOPHOLE[7742314540B, REAL], LOOPHOLE[7743243000B, REAL], LOOPHOLE[7744155200B, REAL], LOOPHOLE[7745054400B, REAL], LOOPHOLE[7745741300B, REAL], LOOPHOLE[7746614600B, REAL], -- 1.211716 LOOPHOLE[7747457040B, REAL], -- 1.237247 LOOPHOLE[7750310640B, REAL], LOOPHOLE[7751132500B, REAL], LOOPHOLE[7751745000B, REAL], LOOPHOLE[7752550100B, REAL], LOOPHOLE[7753344400B, REAL], LOOPHOLE[7754132600B, REAL], LOOPHOLE[7754712500B, REAL] -- 1.402992 ]; Root: PUBLIC PROC [index, arg: REAL] RETURNS [REAL] = { RETURN[Exp[Ln[arg]/index]]; }; Power: PUBLIC PROC [base, exponent: REAL] RETURNS [REAL] = { IF base = 0 THEN IF exponent = 0 THEN RETURN[1] ELSE RETURN[0]; RETURN[Exp[exponent*Ln[base]]]; }; Sin: PUBLIC PROC [radians: REAL] RETURNS [sin: REAL] = { rem: REAL _ ABS[radians]; IF rem > twoPI THEN rem _ GetWithinTwoPI[rem]; { SELECT Real.FixC[rem * fourOverPI] FROM 0 => {GO TO posSin}; 1 => {rem _ PIovr2 - rem; GO TO posCos}; 2 => {rem _ rem - PIovr2; GO TO posCos}; 3 => {rem _ PI - rem; GO TO posSin}; 4 => {rem _ rem - PI; GO TO negSin}; 5 => {rem _ PI3ovr2 - rem; GO TO negCos}; 6 => {rem _ rem - PI3ovr2; GO TO negCos}; 7 => {rem _ twoPI - rem; GO TO negSin}; ENDCASE => RETURN [0.0]; EXITS posSin => {sin _ SinFirstOctant[rem]; IF radians < 0.0 THEN GO TO neg}; negSin => {sin _ SinFirstOctant[rem]; IF radians > 0.0 THEN GO TO neg}; posCos => {sin _ CosFirstOctant[rem]; IF radians < 0.0 THEN GO TO neg}; negCos => {sin _ CosFirstOctant[rem]; IF radians > 0.0 THEN GO TO neg}; }; EXITS neg => sin _ - sin; }; Cos: PUBLIC PROC [radians: REAL] RETURNS [cos: REAL] = { rem: REAL _ ABS[radians]; IF rem > twoPI THEN rem _ GetWithinTwoPI[rem]; { SELECT Real.FixC[rem * fourOverPI] FROM 0 => {GO TO posCos}; 1 => {rem _ PIovr2 - rem; GO TO posSin}; 2 => {rem _ rem - PIovr2; GO TO negSin}; 3 => {rem _ PI - rem; GO TO negCos}; 4 => {rem _ rem - PI; GO TO negCos}; 5 => {rem _ PI3ovr2 - rem; GO TO negSin}; 6 => {rem _ rem - PI3ovr2; GO TO posSin}; 7 => {rem _ twoPI - rem; GO TO posCos}; ENDCASE => cos _ 1.0; EXITS posSin => {cos _ SinFirstOctant[rem]}; negSin => {cos _ SinFirstOctant[rem]; cos _ -cos}; posCos => {cos _ CosFirstOctant[rem]}; negCos => {cos _ CosFirstOctant[rem]; cos _ -cos}; }; }; SinDeg: PUBLIC PROC [degrees: REAL] RETURNS [sin: REAL] = { IF ABS[degrees] > 360 THEN degrees _ GetWithin360[degrees]; sin _ Sin[degrees*radiansPerDegree]; }; CosDeg: PUBLIC PROC [degrees: REAL] RETURNS [cos: REAL] = { degrees _ ABS[degrees]; IF degrees > 360 THEN degrees _ GetWithin360[degrees]; cos _ Cos[degrees*radiansPerDegree]; }; Tan: PUBLIC PROC [radians: REAL] RETURNS [tan: REAL] = { IF ABS[radians] > twoPI THEN radians _ GetWithinTwoPI[radians]; tan _ Sin[radians] / Cos[radians]; }; TanDeg: PUBLIC PROC [degrees: REAL] RETURNS [tan: REAL] = { IF ABS[degrees] > 360 THEN degrees _ GetWithin360[degrees]; tan _ SinDeg[degrees] / CosDeg[degrees]; }; tanPI16: REAL = .1989123673; x2: REAL _ 1/(.414213562); x22: REAL _ x2*x2 + 1; tan3PI16: REAL = .668178638; x4: REAL = 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; ArcTan: PUBLIC PROC [y, x: REAL] RETURNS [radians: REAL] = { 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 < tanPI16 => {t _ v; c _ 0}; < tan3PI16 => {t _ x2 - x22/(x2 + v); c _ PIovr8}; < tan5PI16 => {t _ x4 - x44/(x4 + v); c _ PIovr4}; < tan7PI16 => {t _ x6 - x66/(x6 + v); c _ PI3ovr8}; ENDCASE => {t _ -1/v; c _ PIovr2}; t2 _ t*t; radians _ s*(t*(p0 + t2*(p1 + t2*(p2 + t2*p3))) + c) + q; }; ArcTanDeg: PUBLIC PROC [y, x: REAL] RETURNS [degrees: REAL] = { degrees _ ArcTan[y, x]*degreesPerRadian; }; AlmostZero: PUBLIC PROC [x: REAL, distance: INTEGER [-126..127]] RETURNS [BOOL] = { RETURN[LOOPHOLE[x, IeeeInternal.SingleReal].exp < (distance + IeeeInternal.ExponentBias)]; }; AlmostEqual: PUBLIC PROC [y, x: REAL, distance: INTEGER [-126..0]] RETURNS [BOOL] = { fl: IeeeInternal.SingleReal _ LOOPHOLE[(x - y)]; oldexp: INTEGER _ MAX[ LOOPHOLE[x, IeeeInternal.SingleReal].exp, LOOPHOLE[y, IeeeInternal.SingleReal].exp]; RETURN[fl.exp < (distance + oldexp)]; }; fact9recip: REAL _ 1.0/(INT[1]*2*3*4*5*6*7*8*9); fact8recip: REAL _ 1.0/(INT[1]*2*3*4*5*6*7*8); fact7recip: REAL _ 1.0/(INT[1]*2*3*4*5*6*7); fact6recip: REAL _ 1.0/(INT[1]*2*3*4*5*6); fact5recip: REAL _ 1.0/(INT[1]*2*3*4*5); fact4recip: REAL _ 1.0/(INT[1]*2*3*4); fact3recip: REAL _ 1.0/(INT[1]*2*3); fact2recip: REAL _ 1.0/(INT[1]*2); minEps: REAL _ 1.0 - LOOPHOLE[LOOPHOLE[1.0, LONG CARDINAL]-1, REAL]; sinCosEps: REAL _ SqRt[minEps]; SinFirstOctant: PROC [radians: REAL] RETURNS [REAL] = { IF radians <= sinCosEps THEN RETURN [radians] ELSE { r2: REAL ~ radians*radians; RETURN [(((fact9recip*r2-fact7recip)*r2+fact5recip)*r2-fact3recip)*r2*radians+radians]; }; }; CosFirstOctant: PROC [radians: REAL] RETURNS [REAL] = { IF radians <= sinCosEps THEN RETURN [1.0] ELSE { r2: REAL ~ radians*radians; RETURN [((((fact8recip*r2-fact6recip)*r2+fact4recip)*r2-fact2recip)*r2+1)]; }; }; PIc1: REAL _ 6.28125; -- 402.0/64 (exact representation near twoPI) PIc2: REAL = 1.9353072e-3; -- 2*PI - c1 GetWithinTwoPI: PROC [radians: REAL] RETURNS [REAL] = { xn: REAL _ Real.Fix[radians * rec2pi]; RETURN [(radians - xn*PIc1) - xn*PIc2]; }; GetWithin360: PROC [degrees: REAL] RETURNS [REAL] = { xn: REAL _ Real.Fix[degrees * rec360]; RETURN [degrees - xn*360]; }; 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 October 24, 1984 4:37:17 pm PDT, Plass; New impls for Sin, Cos, SinDeg, CosDeg November 2, 1984, Atkinson; Faster & more accurate SqRt, Sin, Cos, SinDeg, CosDeg ˆRealFnsImpl.mesa Copyright c 1984 by Xerox Corporation. All rights reserved. Stewart on August 27, 1982 1:07 pm Paul Rovner on May 4, 1983 10:13 am Levin, August 8, 1983 4:39 pm Plass, October 24, 1984 4:26:48 pm PDT Russ Atkinson (RRA), November 2, 1984 3:06:16 pm PST NOTE: The compiler is bloody incompetant about floating point! In particular, it cannot deal with constant folding. Therefore, various constants are given as variables to avoid excess computation. Please make these revert to constants when the compiler gets smarter. (RRA) Some useful constants compute e^x using continued fractions ACM Algorithm 229, Morelock, James C., "Elementary functions by continued fractions" Note, if LoopCount > 3, then use the following comments f _ (4*(LoopCount-3) - 2) + r/f; f _ (4*(LoopCount-4) - 2) + r/f; unscaled result is (f+x)/(f-x) scale: multipy by 2^k by adding k to the exponent logxy = Ln(y)/Ln(x) if x = z * 2^m then Ln(x) = m * Ln2 + Ln(z). Here, 1/2 <= z < 1 special case for limit condition (x a power of 2) This declaration obviously depends on IEEE format 32-bit numbers (IeeeInternal.SingleReal). The idea is that the index field has the low bit of the exponent and the high 4 bits of the mantissa, which is used to index a table of initial guesses at the square root, each guess is roughly halfway between the lowest and highest root with the given index, which allows convergence with only two iterations. I don't know where the table originated. RRA: This routine is a faster version of RealOps.SqRt (IeeeFloatB.SqRt). It is faster because it works entirely within the default mode, and performs no funny validity checking. The idea is to lookup a square root guess from the table, adjusting for the exponent, then perform a few iterations to get within a reasonable error. Someday we will get ambitious and share the table with IeeeFloatB. Get the initial guess based on the top 4 bits of the mantissa and the bottom bit of the exponent. Adjust the guess exponent based on adding half of the initial exponent (adjusted for the offset inherent in Ieee format). The result is initially the guess This is really b _ (b + a/b)/2, but much faster This is really b _ (b + a/b)/2, but much faster yth root of x = e^(Ln(x)/y) x^y = e^(y*Ln(x)) We need to have rem IN [0..twoPI] Now select the appropriate formula given the octant. We need to have rem IN [0..twoPI] Now select the appropriate formula given the octant. We get better results if we compute in the degrees domain for a while. We get better results if we compute in the degrees domain for a while. Constants for ArcTan This function is good to 8.7 decimal places and is taken from: Computer Approximations, John F. Hart et.al. pp129(top 2nd col). Helpful internal functions Coefficients for the taylor series for sin and cos. roughly 5.96e-8, the minimum relative error between two different reals RRA: IF radians <= sinEps THEN SinFirstOctant[radians] = radians AND CosFirstOctant[radians] = 1.0 to the precision of our number system. The contribution of the smaller term of two consecutive terms in the Sin or Cos polynomials is down by the square from the larger (and a factor of at least two). So sinCosEps roughly equals 1.726e-4. Testing for this case improves accuracy at small values. This version of Sin is only guaranteed to be accurate within the first octant: [0.0..PI/4], which is approximately [0.0..0.785+]. When radians = PI/4 we have the maximum contribution of the term after the fact9recip term, for a truncation error of roughly 1.76e-9, which is within minEps*Sin[radians] (= 4.2e-8). This version of Cos is only guaranteed to be accurate within the first octant: [0.0..PI/4]. When radians = PI/4 we have the maximum contribution of the terms after the fact8recip term, for a truncation error of roughly 2.46e-8, which is within minEps*Cos[PI/4] (= 4.2e-8). Two constants used to add N*PI with more than normal precision. Let the returned value be R. Then radians <= 0 J radians S xn*twoPI + R & -twoPI <= R <= 0 radians >= 0 J radians S xn*twoPI + R & 0 <= R <= twoPI Let the returned value be R. Then degrees <= 0 J degrees S xn*360 + R & -360 <= R <= 0 degrees >= 0 J degrees S xn*360 + R & 0 <= R <= 360 Κά˜šœ™Jšœ Οmœ1™™>Jšœ@™@—Jšœžœ˜šžœžœžœž˜"šžœžœžœ ˜Jšžœžœ˜Jš žœžœžœžœ žœžœ ˜7——šžœ˜Jš žœžœžœžœ žœžœ ˜9Jšžœžœžœžœ˜7—Jšœžœ˜ šžœž˜ Jšœ˜Jšœ2˜2Jšœ2˜2Jšœ3˜3Jšžœ˜$—J˜ J˜9J˜J˜—š   œžœžœžœžœ žœ˜?Jšœ(˜(Jšœ˜J˜—š  œžœžœžœ žœžœžœ˜SJšžœžœK˜ZJ˜J˜—š  œžœžœžœ žœ žœžœ˜UJšœžœ ˜0šœžœžœ˜Jšžœ"žœ"˜T—Jšžœ˜%J˜J˜—šœ™J˜™3Jšœ žœžœ˜0Kšœ žœžœ˜.Kšœ žœžœ˜,Kšœ žœžœ˜*Kšœ žœžœ ˜(Kšœ žœžœ ˜&Kšœ žœžœ ˜$Kšœ žœžœ˜"K˜—š œžœ žœžœžœžœžœ˜DJšœG™G—šœ žœ˜Jšžœžœžœ#žœΙ™—J˜š  œžœ žœžœžœ˜7JšœΉ™Ήšžœžœžœ žœ˜4Jšœžœ˜JšžœQ˜WJ˜—Jšœ˜J˜—š  œžœ žœžœžœ˜7JšœUžœžœ’žœ™‘šžœžœžœžœ˜0Jšœžœ˜JšžœE˜KJ˜—Jšœ˜J˜—šœ?™?Jšœžœ ‘-˜DJšœžœ‘ ˜'J˜—š  œžœ žœžœžœ˜7šœ"™"Jšœ œ œœ™8Jšœ œ œœ™7—Jšœžœ˜&Jšžœ!˜'J˜J˜—š   œžœ žœžœžœ˜5šœ"™"Jšœ œ œ œ™4Jšœ œ œ œ™3—Jšœžœ˜&Jšžœ˜J˜—J˜—Jšžœ˜J˜—Jšœžœ˜0Jšœžœ1˜LJšœžœ˜2Jšœ"ž˜'J˜NJ˜Q—…—$ΰED