home *** CD-ROM | disk | FTP | other *** search
/ Merciful 3 / Merciful_Release_3.bin / software / e / excalcv1.2reg.lha / ExCalcV1.2 / Source / ExNumbers.mod < prev    next >
Text File  |  1995-10-07  |  23KB  |  987 lines

  1. MODULE ExNumbers;
  2.  
  3. IMPORT io, Cnv := Conversions, S := Strings;
  4.  
  5. CONST
  6.   MaxExp * = 10000;
  7.   MinExp * = -MaxExp;
  8.   HighBoundsManArray * = 52; (* max possible digits--must be multiple of 4. *)
  9.  
  10. TYPE
  11.   ExStatusType * = INTEGER;
  12.  
  13. CONST
  14.   (* ExStatusType values *)
  15.   Okay              *= 0;
  16.   Overflow          *= 1;
  17.   Underflow         *= 2;
  18.   DivideByZero      *= 3;
  19.   TooFewDigits      *= 4;
  20.   TooManyDigits     *= 5;
  21.   IllegalNumber     *= 6;
  22.   UndefinedStorage  *= 7;
  23.   IllegalOperator   *= 8;
  24.   MismatchBraces    *= 9;
  25.  
  26. TYPE
  27.   ExCompareType = INTEGER;
  28.  
  29. CONST
  30.   (* ExCompareType values *)
  31.   ExLess    *= 0;
  32.   ExEqual   *= 1;
  33.   ExGreater *= 2;
  34.  
  35. TYPE
  36.   SignType = SHORTINT;
  37.  
  38. CONST
  39.   (* SignType values *)
  40.   positive *= 0;
  41.   negative *= 1;
  42.  
  43. TYPE
  44.   ManType   * = ARRAY (HighBoundsManArray DIV 4)+2 OF INTEGER;
  45.   ExNumType * = RECORD
  46.                   Man  -: ManType;
  47.                   Sign -: SignType;
  48.                   Zero -: BOOLEAN;
  49.                   Exp  -: INTEGER;
  50.                 END;
  51.  
  52. VAR
  53.   ExStatus * : ExStatusType;
  54.  
  55.   (* Useful constants *)
  56.   e-, ln2-, ln10-, pi-, Ex0-, Ex1-: ExNumType;
  57.  
  58.  
  59. CONST
  60.   MaxLengthNumber = 2 * HighBoundsManArray;
  61.   Dec = 10;
  62.  
  63. VAR
  64.   MaxDigits, MaxQuads : INTEGER;
  65.  
  66.  
  67. PROCEDURE SetMaxDigits *(D : INTEGER);
  68. (* Set maximum digits in extended real numbers -- must be
  69.    a multiple of 4 *)
  70. BEGIN
  71.   IF D < 4 THEN
  72.     MaxDigits := 4;
  73.     ExStatus := TooFewDigits;
  74.   ELSIF D > HighBoundsManArray THEN
  75.     MaxDigits := HighBoundsManArray;
  76.     ExStatus := TooManyDigits;
  77.   ELSE
  78.     MaxDigits := D DIV 4;   (* Force a multiple of 4 *)
  79.     IF D MOD 4 > 0 THEN INC(MaxDigits) END;
  80.     MaxDigits := MaxDigits * 4;
  81.   END;
  82.   MaxQuads := MaxDigits DIV 4;
  83. END SetMaxDigits;
  84.  
  85.  
  86. PROCEDURE ExTimes10 *(VAR A : ExNumType);
  87. (* A := A * 10 -- much faster than ExMult *)
  88. BEGIN
  89.   INC(A.Exp);
  90.   IF A.Exp > MaxExp THEN
  91.     ExStatus := Overflow;
  92.   END;
  93. END ExTimes10;
  94.  
  95.  
  96. PROCEDURE ExDiv10 *(VAR A : ExNumType);
  97. (* A := A / 10 -- much faster than ExDiv *)
  98. BEGIN
  99.   DEC(A.Exp);
  100.   IF A.Exp < MinExp THEN
  101.     ExStatus := Underflow;
  102.   END;
  103. END ExDiv10;
  104.  
  105.  
  106. PROCEDURE IsZero *(A : ExNumType) : BOOLEAN;
  107. VAR
  108.   i : INTEGER;
  109.   Zero : BOOLEAN;
  110. BEGIN
  111.   (* check for zero *)
  112.   i := 0;
  113.   Zero := TRUE;
  114.   WHILE (i <= MaxQuads) AND Zero DO
  115.     IF A.Man[i] # 0 THEN
  116.       Zero := FALSE;
  117.     END;
  118.     INC(i);
  119.   END;
  120.   RETURN Zero;
  121. END IsZero;
  122.  
  123.  
  124. PROCEDURE ExShiftRight(Carry : INTEGER; VAR A : ExNumType);
  125. (* shift all mantissa digits in A to the right one place.
  126.    The most significant digit is replaced with the Carry. *)
  127. VAR
  128.   i : INTEGER;
  129. BEGIN
  130.   (* shift right *)
  131.   FOR i := MaxQuads TO 1 BY -1 DO
  132.     A.Man[i] := A.Man[i] DIV 10 + (A.Man[i-1] MOD 10) * 1000;
  133.   END;
  134.  
  135.   (* put Carry in most significant position *)
  136.   A.Man[0] := A.Man[0] DIV 10 + 1000 * Carry;
  137. END ExShiftRight;
  138.  
  139.  
  140. PROCEDURE ExShiftLeft(VAR A : ExNumType) : INTEGER;
  141. (* shift all mantissa digits in A to the left one place.
  142.    The digit shifted out of the number is returned.
  143.    The least significant digit is replaced with zero. *)
  144. VAR
  145.   i, d : INTEGER;
  146. BEGIN
  147.   (* shift left *)
  148.   d := A.Man[0] DIV 1000;
  149.   FOR i := 0 TO MaxQuads DO
  150.     A.Man[i] := (A.Man[i] MOD 1000) * 10 + A.Man[i+1] DIV 1000;
  151.   END;
  152.  
  153.   (* put zero in least significant position *)
  154.   A.Man[MaxQuads] := (A.Man[MaxQuads] MOD 1000) * 10;
  155.   RETURN d;
  156. END ExShiftLeft;
  157.  
  158.  
  159. PROCEDURE ExChgSign *(VAR A : ExNumType);
  160. (* A := -A *)
  161. BEGIN
  162.   IF A.Sign = positive THEN
  163.     A.Sign := negative;
  164.   ELSE
  165.     A.Sign := positive;
  166.   END;
  167. END ExChgSign;
  168.  
  169.  
  170. PROCEDURE ExAbs *(VAR A : ExNumType);
  171. (* A := ABS(A) *)
  172. BEGIN
  173.   A.Sign := positive;
  174. END ExAbs;
  175.  
  176.  
  177. PROCEDURE ExNorm *(VAR A : ExNumType);
  178. (* Normalise A *)
  179. VAR d : INTEGER;
  180. BEGIN
  181.   (* normalise *)
  182.   IF IsZero(A) THEN
  183.     (* normalize zero *)
  184.     A.Sign := positive;
  185.     A.Exp := 0;
  186.   ELSE
  187.     (* shift mantissa to left until most significant digit is
  188.        non-zero, increment exponent with each shift *)
  189.     WHILE A.Man[0] DIV 1000 = 0 DO
  190.       d := ExShiftLeft(A);
  191.       ExDiv10(A);
  192.     END;
  193.   END;
  194. END ExNorm;
  195.  
  196.  
  197. PROCEDURE GetMaxDigits *() : INTEGER;
  198. (* Get the current number of digits in extended real numbers *)
  199. BEGIN
  200.   RETURN MaxDigits;
  201. END GetMaxDigits;
  202.  
  203.  
  204. PROCEDURE GetExpMant *(x : ExNumType; VAR exp : INTEGER;
  205.                        VAR mant : ExNumType);
  206. (* Returned `mant' number will be between -10.0 and 10.0 *)
  207. BEGIN
  208.   exp := x.Exp;
  209.   mant := x;
  210.   mant.Exp := 0;
  211. END GetExpMant;
  212.  
  213.  
  214. PROCEDURE PutDigit(VAR A : INTEGER; Digit, Index : INTEGER);
  215. BEGIN
  216.   IF Index = 0 THEN
  217.     A := A MOD 1000 + Digit * 1000;
  218.   ELSIF Index = 1 THEN
  219.     A := A DIV 1000 * 1000 + A MOD 100 + Digit * 100;
  220.   ELSIF Index = 2 THEN
  221.     A := A DIV 100 * 100 + A MOD 10 + Digit * 10;
  222.   ELSE
  223.     A := A DIV 10 * 10 + Digit;
  224.   END;
  225. END PutDigit;
  226.  
  227.  
  228. PROCEDURE ExTrunc *(VAR A : ExNumType);
  229. (* Truncate A so no decimal places are kept. *)
  230. VAR
  231.   i : INTEGER;
  232. BEGIN
  233.   IF A.Exp+1 < 0 THEN A := Ex0; RETURN END;
  234.   FOR i := A.Exp+1 TO MaxDigits-1 DO
  235.     (* zero these digits *)
  236.     PutDigit(A.Man[i DIV 4], 0, i MOD 4);
  237.   END;
  238. END ExTrunc;
  239.  
  240.  
  241. PROCEDURE ExFrac *(VAR A : ExNumType);
  242. (* Keep only the fraction portion of A. *)
  243. VAR
  244.   i : INTEGER;
  245. BEGIN
  246.   FOR i := 0 TO A.Exp DO (* zero these digits *)
  247.     PutDigit(A.Man[i DIV 4], 0, i MOD 4);
  248.   END;
  249.   ExNorm(A);             (* normalize the fraction *)
  250. END ExFrac;
  251.  
  252.  
  253. PROCEDURE ExToLongInt *(A : ExNumType) : LONGINT;
  254. (* Convert the extended real number `A' into a INTEGER --
  255.    saturating if necessary. *)
  256. CONST
  257.   MaxDigits = 10;
  258. VAR
  259.   Cnt : INTEGER;
  260.   Int : LONGINT;
  261.   Digit : INTEGER;
  262.   Negative : BOOLEAN;
  263. BEGIN
  264.   Negative := FALSE;
  265.   IF A.Sign = negative THEN
  266.     Negative := TRUE;
  267.     ExAbs(A);
  268.   END;
  269.   IF A.Exp < 0 THEN
  270.     Int := 0;
  271.   ELSIF A.Exp >= MaxDigits THEN
  272.     Int := MAX(LONGINT);
  273.   ELSE
  274.     Int := 0;
  275.     FOR Cnt := 0 TO A.Exp DO
  276.       Digit := ExShiftLeft(A);
  277.       IF Cnt = MaxDigits-1 THEN
  278.         IF Int > MAX(LONGINT) DIV 10 THEN
  279.           RETURN Int;
  280.         END;
  281.         IF (Int = MAX(LONGINT) DIV 10) & (Digit > 6) THEN
  282.           Digit := 6;
  283.         END;
  284.       END;
  285.       Int := Int * 10 + Digit;
  286.     END;
  287.   END;
  288.   IF Negative THEN
  289.     RETURN -Int;
  290.   ELSE
  291.     RETURN Int;
  292.   END;
  293. END ExToLongInt;
  294.  
  295.  
  296. PROCEDURE ExCompare *(A, B : ExNumType) : ExCompareType;
  297. (* Compares the two extended real numbers. *)
  298. VAR
  299.   Done : BOOLEAN;
  300.   i : INTEGER;
  301. BEGIN
  302.   IF A.Sign # B.Sign THEN
  303.     (* A and B have different signs *)
  304.     IF A.Sign = positive THEN
  305.       (* A and B have different signs and A is positive so A>B *)
  306.       RETURN ExGreater;
  307.     ELSE
  308.       (* A and B have different signs and A is negative so A<B *)
  309.       RETURN ExLess;
  310.     END;
  311.   ELSE
  312.     (* A and B have the same sign *)
  313.     IF (A.Exp # B.Exp) & NOT IsZero(B) & NOT IsZero(A) THEN
  314.       IF A.Exp > B.Exp THEN
  315.         (* A exponent > B exponent *)
  316.         IF A.Sign = positive THEN
  317.           RETURN ExGreater;
  318.         ELSE
  319.           RETURN ExLess;
  320.         END;
  321.       ELSE
  322.         (* A exponent <= B exponent *)
  323.         IF A.Sign = positive THEN
  324.           RETURN ExLess;
  325.         ELSE
  326.           RETURN ExGreater;
  327.         END;
  328.       END;
  329.     ELSE
  330.       (* A & B have same sign and A exponent = B exponent *)
  331.       Done := FALSE;
  332.       i := 0;
  333.  
  334.       (* compare each digit until a difference is found or
  335.          we reach the end *)
  336.       WHILE (i <= MaxQuads) AND NOT Done DO
  337.         IF A.Man[i] # B.Man[i] THEN
  338.           Done := TRUE;
  339.         ELSE
  340.           INC(i);
  341.         END;
  342.       END;
  343.       IF i > MaxQuads THEN
  344.         (* end reached and all digits match *)
  345.         RETURN ExEqual;
  346.       ELSE
  347.         (* compare different digits *)
  348.         IF A.Man[i] < B.Man[i] THEN
  349.           IF A.Sign = positive THEN
  350.             RETURN ExLess;
  351.           ELSE
  352.             RETURN ExGreater;
  353.           END;
  354.         ELSE
  355.           IF A.Sign = positive THEN
  356.             RETURN ExGreater;
  357.           ELSE
  358.             RETURN ExLess;
  359.           END;
  360.         END;
  361.       END;
  362.     END;
  363.   END;
  364. END ExCompare;
  365.  
  366.  
  367. PROCEDURE ExMin *(VAR A : ExNumType; B, C : ExNumType);
  368. (* Return the smaller of B and C in A *)
  369. BEGIN
  370.   IF ExCompare(B, C) = ExGreater THEN
  371.     A := C;
  372.   ELSE
  373.     A := B;
  374.   END;
  375. END ExMin;
  376.  
  377.  
  378. PROCEDURE ExMax *(VAR A : ExNumType; B, C : ExNumType);
  379. (* Return the larger of B and C in A *)
  380. BEGIN
  381.   IF ExCompare(B, C) = ExLess THEN
  382.     A := C;
  383.   ELSE
  384.     A := B;
  385.   END;
  386. END ExMax;
  387.  
  388.  
  389. PROCEDURE ExAddUtility(VAR A : ExNumType; B, C : ExNumType);
  390. (* A := ABS(B) + ABS(C) *)
  391. VAR
  392.   i, j, joff, carry, quad, total : INTEGER;
  393.   Exl1, Ex2 : ExNumType;
  394. BEGIN
  395.   IF IsZero(B) THEN
  396.     A := C;
  397.   ELSIF IsZero(C) THEN
  398.     A := B;
  399.   ELSE
  400.     IF B.Exp > C.Exp THEN
  401.       Exl1 := B;
  402.       Ex2 := C;
  403.     ELSE
  404.       Exl1 := C;
  405.       Ex2 := B;
  406.     END;
  407.     A := Ex0;
  408.     A.Exp := Exl1.Exp;
  409.     carry := 0;
  410.  
  411.     (* shift smallest number until quad-aligned relative to
  412.        larger number *)
  413.     j := (Exl1.Exp - Ex2.Exp) MOD 4;
  414.     FOR i := j TO 1 BY -1 DO
  415.       ExShiftRight(0, Ex2);
  416.       INC(Ex2.Exp);
  417.     END;
  418.     joff := (Ex2.Exp - Exl1.Exp) DIV 4;
  419.  
  420.     (* add the two numbers together *)
  421.     FOR i := MaxQuads TO 0 BY -1 DO
  422.       (* j = index to Ex2 *)
  423.       j := i + joff;
  424.  
  425.       (* check that j falls within array bounds *)
  426.       IF (j >= 0) AND (j <= MaxQuads) THEN
  427.         (* get quad digit from Ex2 *)
  428.         quad := Ex2.Man[j];
  429.       ELSE
  430.         (* j is outside array bounds, use 0 for quad digit *)
  431.         quad := 0;
  432.       END;
  433.  
  434.       (* perform addition with carry *)
  435.       total := Exl1.Man[i] + quad + carry;
  436.  
  437.       (* check for carry *)
  438.       IF total >= 10000 THEN
  439.         DEC(total, 10000);
  440.         carry := 1;
  441.       ELSE
  442.         carry := 0;
  443.       END;
  444.       A.Man[i] := total;
  445.     END;
  446.  
  447.     (* handle final carry *)
  448.     IF carry = 1 THEN
  449.       (* shift carry into top of mantissa *)
  450.       ExShiftRight(carry, A);
  451.  
  452.       (* multiply by ten to update exponent *)
  453.       ExTimes10(A);
  454.     END;
  455.   END;
  456.  
  457.   (* set ExStatus *)
  458.   IF A.Exp > MaxExp THEN
  459.     ExStatus := Overflow;
  460.   END;
  461. END ExAddUtility;
  462.  
  463.  
  464. PROCEDURE ExSubUtility(VAR A : ExNumType; B, C : ExNumType);
  465. (* A := ABS(B) - ABS(C) *)
  466. VAR
  467.   PositiveResult : BOOLEAN;
  468.   i, j, joff, borrow, quad, result : INTEGER;
  469.   Exl1, Ex2 : ExNumType;
  470. BEGIN
  471.   ExAbs(B);
  472.   ExAbs(C);
  473.   IF IsZero(B) THEN
  474.     A := C;
  475.   ELSIF IsZero(C) THEN
  476.     A := B;
  477.   ELSE
  478.     IF B.Exp > C.Exp THEN
  479.       Exl1 := B;
  480.       Ex2 := C;
  481.     ELSE
  482.       Exl1 := C;
  483.       Ex2 := B;
  484.     END;
  485.     PositiveResult := ExCompare(Exl1, Ex2) = ExGreater;
  486.     A := Ex0;
  487.     A.Exp := Exl1.Exp;
  488.     borrow := 0;
  489.  
  490.     (* shift smallest number until quad-aligned relative to
  491.        larger number *)
  492.     j := (Exl1.Exp - Ex2.Exp) MOD 4;
  493.     FOR i := j TO 1 BY -1 DO
  494.       ExShiftRight(0, Ex2);
  495.       INC(Ex2.Exp);
  496.     END;
  497.     joff := (Ex2.Exp - Exl1.Exp) DIV 4;
  498.  
  499.     (* subtract the two numbers *)
  500.     FOR i := MaxQuads TO 0 BY -1 DO
  501.       (* j = index to Ex2 *)
  502.       j := i + joff;
  503.  
  504.       (* check that j falls within array bounds *)
  505.       IF (j >= 0) AND (j <= MaxQuads) THEN
  506.         (* get quad from Ex2 *)
  507.         quad := Ex2.Man[j];
  508.       ELSE
  509.         (* j is outside array bounds, use 0 for quad *)
  510.         quad := 0;
  511.       END;
  512.  
  513.       (* perform subtraction with borrow *)
  514.       IF PositiveResult THEN
  515.         result := Exl1.Man[i] - quad - borrow;
  516.       ELSE
  517.         result := quad - Exl1.Man[i] - borrow;
  518.       END;
  519.  
  520.       (* check for borrow *)
  521.       IF result < 0 THEN
  522.         INC(result, 10000);
  523.         borrow := 1;
  524.       ELSE
  525.         borrow := 0;
  526.       END;
  527.       A.Man[i] := result;
  528.     END;
  529.   END;
  530.  
  531.   (* normalise *)
  532.   ExNorm(A);
  533.  
  534.   (* adjust sign *)
  535.   IF ExCompare(B, C) = ExLess THEN
  536.     ExChgSign(A);
  537.   END;
  538. END ExSubUtility;
  539.  
  540.  
  541. PROCEDURE ExAdd *(VAR A : ExNumType; B, C : ExNumType);
  542. (* A = B + C *)
  543. BEGIN
  544.   IF B.Sign = C.Sign THEN
  545.     (* B and C have the same sign -- just add *)
  546.     ExAddUtility(A, B, C);
  547.     IF B.Sign = negative THEN
  548.       ExChgSign(A);
  549.     END;
  550.   ELSE
  551.     (* B and C have different signs *)
  552.     IF B.Sign = positive THEN
  553.       ExSubUtility(A, B, C);
  554.     ELSE
  555.       ExSubUtility(A, C, B);
  556.     END;
  557.   END;
  558. END ExAdd;
  559.  
  560.  
  561. PROCEDURE ExSub *(VAR A : ExNumType; B, C : ExNumType);
  562. (* A = B - C *)
  563. BEGIN
  564.   ExChgSign(C);   (* A = B + (-C) *)
  565.   ExAdd(A, B, C);
  566. END ExSub;
  567.  
  568.  
  569. PROCEDURE ExRound *(VAR A : ExNumType; D : INTEGER);
  570. (* A := Round(A) *)
  571. VAR
  572.   cindex, index, digit, i : INTEGER;
  573.   Exl : ExNumType;
  574. BEGIN
  575.   IF D <= MaxDigits-1 THEN
  576.     index := (D+1) DIV 4;
  577.     digit := A.Man[index];
  578.     cindex := (D + 1) MOD 4;
  579.     IF cindex = 0 THEN
  580.       digit := digit DIV 1000;
  581.     ELSIF cindex = 1 THEN
  582.       digit := digit DIV 100;
  583.     ELSIF cindex = 2 THEN
  584.       digit := digit DIV 10;
  585.     END;
  586.     IF digit MOD 10 >= 5 THEN
  587.       (* round up *)
  588.       Exl := Ex1;
  589.       Exl.Exp := A.Exp - D;
  590.       IF A.Sign = negative THEN
  591.         ExChgSign(Exl);
  592.       END;
  593.       ExAdd(A, A, Exl);
  594.     END;
  595.  
  596.     (* make remaining digits zero *)
  597.     IF cindex = 0 THEN
  598.       A.Man[index] := 0;
  599.     ELSIF cindex = 1 THEN
  600.       A.Man[index] := A.Man[index] DIV 1000 * 1000;
  601.     ELSIF cindex = 2 THEN
  602.       A.Man[index] := A.Man[index] DIV 100 * 100;
  603.     ELSIF cindex = 3 THEN
  604.       A.Man[index] := A.Man[index] DIV 10 * 10;
  605.     END;
  606.     FOR i := index+1 TO MaxQuads DO
  607.       A.Man[i] := 0;
  608.     END;
  609.   END;
  610. END ExRound;
  611.  
  612.  
  613. PROCEDURE ExMult *(VAR A : ExNumType; B, C : ExNumType);
  614. (* Return B * C *)
  615. VAR
  616.   i, j, carry : INTEGER;
  617.   product : LONGINT;
  618.   Exl : ExNumType;
  619. BEGIN
  620.   IF (ExCompare(B,Ex0) = ExEqual) OR (ExCompare(C,Ex0) = ExEqual) THEN
  621.     (* multiplication by zero *)
  622.     A := Ex0;
  623.   ELSIF ExCompare(C,Ex1) = ExEqual THEN
  624.     A := B;
  625.   ELSIF ExCompare(B,Ex1) = ExEqual THEN
  626.     A := C;
  627.   ELSE
  628.     (* real multiplication *)
  629.     A := Ex0;
  630.     FOR i := MaxQuads TO 0 BY -1 DO
  631.       Exl := Ex0;
  632.       Exl.Exp := B.Exp + C.Exp - i * 4 - 3;
  633.       carry := 0;
  634.       FOR j := MaxQuads TO 0 BY -1 DO
  635.         product := LONG(B.Man[j]) * LONG(C.Man[i]) + LONG(carry);
  636.         Exl.Man[j] := SHORT(product MOD 10000);
  637.         carry := SHORT(product DIV 10000);
  638.       END;
  639.  
  640.       (* check for final carry *)
  641.       WHILE carry > 0 DO
  642.         ExShiftRight(carry MOD 10, Exl);
  643.         ExTimes10(Exl);
  644.         carry := carry DIV 10;
  645.       END;
  646.  
  647.       (* perform summation *)
  648.       ExAddUtility(A, A, Exl);
  649.     END;
  650.  
  651.     (* adjust product sign *)
  652.     IF B.Sign # C.Sign THEN
  653.       ExChgSign(A);
  654.     END;
  655.   END;
  656. END ExMult;
  657.  
  658.  
  659. PROCEDURE ExDiv *(VAR A : ExNumType; B, C : ExNumType);
  660. (* A := B / C *)
  661. VAR
  662.   i, j : INTEGER;
  663.   quotient : LONGINT;
  664.   Exl1, Ex2 : ExNumType;
  665. BEGIN
  666.   IF IsZero(C) THEN
  667.     (* attempt to divide by zero *)
  668.     ExStatus := DivideByZero;
  669.   ELSIF IsZero(B) THEN
  670.     (* dividend = 0 *)
  671.     A := Ex0;
  672.   ELSIF ExCompare(C,Ex1) = ExEqual THEN
  673.     (* divisor = 1 *)
  674.     A := B;
  675.   ELSE
  676.     (* real division *)
  677.     A := Ex0;
  678.     A.Exp := B.Exp - C.Exp;
  679.  
  680.     (* adjust quotient sign *)
  681.     IF B.Sign # C.Sign THEN
  682.       ExChgSign(A);
  683.     END;
  684.  
  685.     (* let Exl1 = ABS(B) / magnitude of B *)
  686.     Exl1 := B;
  687.     ExAbs(Exl1);
  688.     Exl1.Exp := 0;
  689.  
  690.     (* let Ex2 = ABS(C) / magnitude of C *)
  691.     Ex2 := C;
  692.     ExAbs(Ex2);
  693.     Ex2.Exp := 0;
  694.  
  695.     (* actual division *)
  696.     FOR i := 0 TO MaxDigits-1 DO
  697.       quotient := 0;
  698.       WHILE ExCompare(Exl1, Ex2) >= ExEqual DO
  699.         INC(quotient);
  700.         ExSubUtility(Exl1, Exl1, Ex2);
  701.       END;
  702.       A.Man[i DIV 4] := A.Man[i DIV 4] * 10 + SHORT(quotient);
  703.       ExDiv10(Ex2);
  704.     END;
  705.  
  706.     (* normalize quotient *)
  707.     ExNorm(A);
  708.   END;
  709. END ExDiv;
  710.  
  711.  
  712. (* $CopyArrays- *)
  713. PROCEDURE StrToExNum *(Str : ARRAY OF CHAR; VAR A : ExNumType);
  714. (* Convert the string `Str' into an extended real number in A. *)
  715. VAR
  716.   Exp, NumbIndex, InCnt, EndCnt : INTEGER;
  717.   ZeroFlag, NegativeExponent, LeftSide, InExponent : BOOLEAN;
  718.   Done, NegExponent : BOOLEAN;
  719.   ActiveChar : CHAR;
  720.  
  721.   PROCEDURE SetDigit(VAR Numb : INTEGER);
  722.   BEGIN
  723.     Numb := Numb * 10 + ORD(Str[InCnt]) - ORD('0');
  724.   END SetDigit;
  725.  
  726. BEGIN
  727.   (* initialize a few counters and stuff *)
  728.   A := Ex0;
  729.   InCnt := 0;             (* character counter *)
  730.   Exp := 0;               (* working exponent *)
  731.   LeftSide := TRUE;
  732.   InExponent := FALSE;
  733.   ZeroFlag := TRUE;
  734.   NegativeExponent := FALSE;
  735.   EndCnt := SHORT(S.Length(Str));
  736.   NumbIndex := 0;
  737.  
  738.   (* set the sign of `A' to a negative -- if needed *)
  739.   WHILE (InCnt < EndCnt) & (Str[InCnt] = ' ') DO INC(InCnt) END;
  740.   IF Str[InCnt] = '-' THEN
  741.     A.Sign := negative;
  742.     INC(InCnt);
  743.   END;
  744.   WHILE InCnt < EndCnt DO
  745.     ActiveChar := Str[InCnt];
  746.     IF (ActiveChar >= '0') & (ActiveChar <= '9') THEN
  747.       IF InExponent THEN
  748.         SetDigit(Exp);
  749.       ELSE
  750.         IF NumbIndex < MaxDigits THEN  (* enter a digit *)
  751.           SetDigit(A.Man[NumbIndex DIV 4]);
  752.         END;
  753.         IF ZeroFlag & (Str[InCnt] # '0') THEN
  754.           ZeroFlag := FALSE;
  755.         END;
  756.         IF NOT ZeroFlag THEN
  757.           INC(NumbIndex);
  758.           IF LeftSide THEN INC(A.Exp) END;
  759.         ELSE
  760.           IF NOT LeftSide & (A.Exp <= 0) THEN DEC(A.Exp) END;
  761.         END;
  762.       END;
  763.     ELSIF ActiveChar = '.' THEN
  764.       IF ~LeftSide THEN ExStatus := IllegalNumber END;
  765.       LeftSide := FALSE;
  766.     ELSIF ActiveChar = 'E' THEN
  767.       InExponent := TRUE;
  768.       IF Str[InCnt+1] = '-' THEN
  769.         NegativeExponent := TRUE;
  770.         INC(InCnt);
  771.       ELSIF Str[InCnt+1] = '+' THEN
  772.         INC(InCnt);
  773.       END;
  774.     ELSIF ActiveChar = ' ' THEN
  775.       (* do nothing if blanks are encountered *)
  776.     ELSE
  777.       ExStatus := IllegalNumber;
  778.     END; (* IF *)
  779.     INC(InCnt);
  780.   END;
  781.  
  782.   (* fix up the last quad digits *)
  783.   WHILE (NumbIndex DIV 4 <= MaxQuads) & (NumbIndex MOD 4 > 0) DO
  784.     A.Man[NumbIndex DIV 4] := A.Man[NumbIndex DIV 4] * 10;
  785.     INC(NumbIndex);
  786.   END;
  787.  
  788.   (* Do some final fixes to the exponent *)
  789.   IF NegativeExponent THEN
  790.     DEC(A.Exp, Exp);
  791.   ELSE
  792.     INC(A.Exp, Exp);
  793.   END;
  794.   DEC(A.Exp);
  795.  
  796.   (* Ensure valid zero value *)
  797.   IF IsZero(A) THEN A := Ex0 END;
  798. END StrToExNum;
  799.  
  800.  
  801. PROCEDURE GetDigit(VAR ExpStr : ARRAY OF CHAR; VAR StrCnt : INTEGER;
  802.                    A : ExNumType; VAR ManIndex : INTEGER) : CHAR;
  803. VAR Quad : LONGINT;
  804.     Ok : BOOLEAN;
  805. BEGIN
  806.   (* Passing all parameters due to a bug in Oberon-2 when this
  807.      was a local procedure *)
  808.   INC(StrCnt);
  809.   IF StrCnt = 4 THEN (* get a quad of digits *)
  810.     Quad := A.Man[ManIndex];
  811.     Ok := Cnv.IntToStr(Quad,ExpStr,Dec,5,'0');
  812.     S.Delete(ExpStr, 0, 1);   (* remove leading digit *)
  813.     INC(ManIndex);
  814.     StrCnt := 0;
  815.   END;
  816.   RETURN ExpStr[StrCnt];
  817. END GetDigit;
  818.  
  819.  
  820. PROCEDURE ExNumToStr *(A : ExNumType; Decimal, ExpWidth : INTEGER;
  821.                        VAR Str : ARRAY OF CHAR);
  822. (* Convert the extended real number into a string `S'. *)
  823. VAR
  824.   pos, ManIndex, StrCnt, InCnt, Aexp, MaxExpWidth : INTEGER;
  825.   ExpStr : ARRAY 41 OF CHAR;
  826.   FixPoint, Ok : BOOLEAN;
  827.  
  828.   PROCEDURE ConcatChar(ch : CHAR);
  829.   BEGIN
  830.     Str[pos] := ch;
  831.     INC(pos);
  832.   END ConcatChar;
  833.  
  834. BEGIN
  835.   (* initialize a few parameters *)
  836.   pos := 0;
  837.   StrCnt := 3;
  838.   ManIndex := 0;
  839.   ExpStr := '';
  840.  
  841.   (* force scientific notation for numbers too small or too large *)
  842.   Aexp := ABS(A.Exp);
  843.   MaxExpWidth := ExpWidth;
  844.   IF ((ExpWidth = 0) AND (Aexp > MaxDigits)) OR (ExpWidth > 0) THEN
  845.     (* force scientific notation *)
  846.     IF Aexp > 9999 THEN ExpWidth := 5
  847.     ELSIF Aexp > 999 THEN ExpWidth := 4
  848.     ELSIF Aexp > 99 THEN ExpWidth := 3
  849.     ELSIF Aexp > 9 THEN ExpWidth := 2
  850.     ELSE ExpWidth := 1
  851.     END;
  852.   END;
  853.   IF MaxExpWidth < ExpWidth THEN MaxExpWidth := ExpWidth END;
  854.  
  855.   (* add the negative sign to the number *)
  856.   IF A.Sign = negative THEN ConcatChar('-') END;
  857.  
  858.   (* ensure we don't exceed the maximum digits *)
  859.   FixPoint := Decimal # 0;
  860.   IF (Decimal > MaxDigits) OR NOT FixPoint THEN
  861.     Decimal := MaxDigits-1;
  862.   END;
  863.  
  864.   (* convert the number into scientific notation *)
  865.   IF MaxExpWidth > 0 THEN
  866.     ExRound(A, Decimal);    (* round to appropriate decimal places *)
  867.     ConcatChar(GetDigit(ExpStr, StrCnt, A, ManIndex)); (* leading digit *)
  868.     ConcatChar('.');        (* decimal point *)
  869.     FOR InCnt := 1 TO Decimal DO
  870.       ConcatChar(GetDigit(ExpStr, StrCnt, A, ManIndex));     (* add following digits *)
  871.     END;
  872.  
  873.     (* add the exponent *)
  874.     ConcatChar('E');
  875.     IF A.Exp >= 0 THEN ConcatChar('+') ELSE ConcatChar('-') END;
  876.     ConcatChar(0X);                        (* terminate the string *)
  877.  
  878.     Ok := Cnv.IntToStr(Aexp,ExpStr,Dec,SHORT(MaxExpWidth),'0');
  879.     S.Append(Str, ExpStr);
  880.   ELSE
  881.     (* format a non-scientific number *)
  882.     ExRound(A, Decimal+A.Exp); (* round to decimal places *)
  883.     IF A.Exp < 0 THEN
  884.       ConcatChar('0');         (* leading digit *)
  885.       ConcatChar('.');         (* decimal point *)
  886.       FOR InCnt := 2 TO ABS(A.Exp) DO   (* pad with leading zeros *)
  887.         ConcatChar('0');
  888.       END;
  889.       INC(Decimal, A.Exp+1);
  890.     END;
  891.     InCnt := 0;
  892.     REPEAT
  893.       ConcatChar(GetDigit(ExpStr, StrCnt, A, ManIndex));
  894.       IF InCnt > A.Exp THEN
  895.         DEC(Decimal);
  896.       ELSIF InCnt = A.Exp THEN
  897.         ConcatChar('.');
  898.       END;
  899.       INC(InCnt);
  900.     UNTIL (InCnt = MaxDigits) OR (Decimal = 0);
  901.     ConcatChar(0X);
  902.  
  903.     (* remove any trailing zeros and unneeded digits *)
  904.     InCnt := pos - 2;
  905.     WHILE (InCnt > 1) & (Str[InCnt] = '0') & NOT FixPoint DO
  906.       Str[InCnt] := 0X;
  907.       DEC(InCnt);
  908.     END;
  909.   END;
  910. END ExNumToStr;
  911.  
  912.  
  913. PROCEDURE WriteExNum *(A : ExNumType;
  914.                        Width, Decimal, ExpWidth : INTEGER);
  915. (* Write out A to the current output stream in a field of
  916.    `Width' characters, with `Decimal' decimal places, and
  917.    `ExpWidth' exponent width. *)
  918. VAR
  919.   Str : ARRAY MaxLengthNumber+1 OF CHAR;
  920.   i, len : INTEGER;
  921. BEGIN
  922.   ExNumToStr(A, Decimal, ExpWidth, Str);
  923.   len := SHORT(S.Length(Str));
  924.   IF Width >= len THEN
  925.     FOR i := 1 TO Width-len DO io.Write(" ") END;
  926.   END;
  927.   io.WriteString(Str);
  928. END WriteExNum;
  929.  
  930.  
  931. PROCEDURE ExNumb *(LeftMan : LONGINT; RightMan : LONGINT;
  932.                    ExpShift : INTEGER; VAR A : ExNumType);
  933. (* create an extended real number which has LeftMan to the left
  934.    of the decimal point and RightMan to the right. The ExpShift
  935.    quantity can shift the decimal point to the right for negative
  936.    values; to the left for positive values. *)
  937. VAR
  938.   i : INTEGER;
  939. BEGIN
  940.   A := Ex0;
  941.   IF LeftMan < 0 THEN
  942.     A.Sign := negative;
  943.     LeftMan := -LeftMan;
  944.   END;
  945.   WHILE RightMan # 0 DO
  946.     ExShiftRight(SHORT(RightMan MOD 10), A);(* shift right 1 position *)
  947.     RightMan := RightMan DIV 10;
  948.   END;
  949.   WHILE LeftMan # 0 DO
  950.     ExShiftRight(SHORT(LeftMan MOD 10), A); (* shift right 1 position *)
  951.     ExTimes10(A);                    (* adjust the exponent *)
  952.     LeftMan := LeftMan DIV 10;
  953.   END;
  954.   ExDiv10(A);                        (* final exponent adjust *)
  955.   INC(A.Exp, ExpShift);              (* shift the decimal point *)
  956.   IF A.Exp > MaxExp THEN             (* signal any errors *)
  957.     ExStatus := Overflow;
  958.   ELSIF A.Exp < MinExp THEN
  959.     ExStatus := Underflow;
  960.   END;
  961. END ExNumb;
  962.  
  963.  
  964. BEGIN
  965.   (* create extended number 0 *)
  966.   Ex0.Sign := positive;
  967.   FOR MaxDigits := 0 TO LEN(Ex0.Man)-1 DO
  968.     Ex0.Man[MaxDigits] := 0;
  969.   END;
  970.   Ex0.Exp := 0;
  971.  
  972.   (* default to max number of digits *)
  973.   SetMaxDigits(HighBoundsManArray);
  974.  
  975.   (* create some extended number constants *)
  976.   ExNumb(1, 0, 0, Ex1);     (* 1.0 *)
  977.  
  978.   StrToExNum(
  979.   "3.14159265358979323846264338327950288419716939937511", pi);
  980.   StrToExNum(
  981.   "2.71828182845904523536028747135266249775724709369996", e);
  982.   StrToExNum(
  983.   "0.69314718055994530941723212145817656807550013436026", ln2);
  984.   StrToExNum(
  985.   "2.30258509299404568401799145468436420760110148862877", ln10);
  986. END ExNumbers.
  987.