home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 5 / FreshFish_July-August1994.bin / bbs / gnu / dc-0.2-src.lha / src / amiga / dc-0.2 / decimal.c < prev    next >
C/C++ Source or Header  |  1993-05-21  |  30KB  |  1,236 lines

  1. /* 
  2.  * Arbitrary precision decimal arithmetic.
  3.  *
  4.  * Copyright (C) 1984 Free Software Foundation, Inc.
  5.  *
  6.  * This program is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation; either version 2, or (at your option)
  9.  * any later version.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program; if not, you can either send email to this
  18.  * program's author (see below) or write to: The Free Software Foundation,
  19.  * Inc.; 675 Mass Ave. Cambridge, MA 02139, USA.
  20.  */
  21.  
  22. /* Some known problems:
  23.  
  24.     Another problem with decimal_div is found when you try to
  25.     divide a number with > scale fraction digits by 1.  The
  26.     expected result is simply truncation, but all sorts of things
  27.     happen instead.  An example is that the result of .99999998/1
  28.     with scale set to 6 is .000001
  29.         
  30.     There are some problems in the behavior of the decimal package
  31.     related to printing and parsing.  The
  32.     printer is weird about very large output radices, tending to want
  33.     to output single ASCII characters for any and all digits (even
  34.     in radices > 127).  The UNIX bc approach is to print digit groups
  35.     separated by spaces.  There is a rather overwrought workaround in
  36.     the function decputc() in bcmisc.c, but it would be better if
  37.     decimal.c got a fix for this.  */
  38.  
  39. /* For stand-alone testing, compile with -DTEST.
  40.    This DTESTable feature defines a `main' function
  41.    which is a simple loop that accepts input of the form
  42.    number space op space number newline
  43.    where op is +, -, *, /, %, p or r,
  44.    and performs the operation and prints the operands and result.
  45.    `p' means print the first number in the radix spec'd by the second.
  46.    `r' means read the first one in the radix specified by the second
  47.    (and print the result in decimal).
  48.    Divide in this test keeps three fraction digits. */
  49.  
  50. #include "decimal.h"
  51.  
  52. #define MAX(a, b) (((a) > (b) ? (a) : (b)))
  53.  
  54. /* Some constant decimal numbers */
  55.  
  56. struct decimal decimal_zero = {0, 0, 0, 0, 0};
  57.  
  58. struct decimal decimal_one = {0, 0, 1, 0, 1};
  59.  
  60. /*** Assumes RADIX is even ***/
  61. struct decimal decimal_half = {0, 1, 0, 0, RADIX / 2};
  62.  
  63. decimal static decimal_add1 (), decimal_sub1 ();
  64. static void add_scaled ();
  65. static int subtract_scaled ();
  66.  
  67. /* Create and return a decimal number that has `before' digits before
  68.    the decimal point and `after' digits after.  The digits themselves are
  69.    initialized to zero.  */
  70.  
  71. decimal
  72. make_decimal (before, after)
  73.      int before, after;
  74. {
  75.   decimal result;
  76.   if (before >= 1<<16)
  77.     {
  78.       decimal_error ("%d too many decimal digits", before);
  79.       return 0;
  80.     }
  81.   if (after >= 1<<15)
  82.     {
  83.       decimal_error ("%d too many decimal digits", after);
  84.       return 0;
  85.     }
  86.   result = (decimal) malloc (sizeof (struct decimal) + before + after - 1);
  87.   result->sign = 0;
  88.   result->before = before;
  89.   result->after = after;
  90.   result->refcnt = 0;
  91.   bzero (result->contents, before + after);
  92.   return result;
  93. }
  94.  
  95. /* Create a copy of the decimal number `b' and return it.  */
  96.  
  97. decimal
  98. decimal_copy (b)
  99.      decimal b;
  100. {
  101.   decimal result = make_decimal (b->before, b->after);
  102.   bcopy (b->contents, result->contents, LENGTH(b));
  103.   result->sign = b->sign;
  104.   return result;
  105. }
  106.  
  107. /* Copy a decimal number `b' but extend or truncate to exactly
  108.    `digits' fraction digits. */
  109.  
  110. static decimal
  111. decimal_copy_1 (b, digits)
  112.      decimal b;
  113.      int digits;
  114. {
  115.   if (digits > b->after)
  116.     {
  117.       decimal result = make_decimal (b->before, digits);
  118.       bcopy (b->contents, result->contents + (digits - (int) b->after), LENGTH(b));
  119.       return result;
  120.     }
  121.   else
  122.     return decimal_round_digits (b, digits);
  123. }
  124.  
  125. /* flush specified number `digits' of trailing fraction digits,
  126.    and flush any trailing fraction zero digits exposed after they are gone.
  127.    The number `b' is actually modified; no new storage is allocated.
  128.    That is why this is not global.  */
  129.  
  130. static void
  131. flush_trailing_digits (b, digits)
  132.      decimal b;
  133.      int digits;
  134. {
  135.   int flush = digits;
  136.   int maxdig = b->after;
  137.  
  138.   while (flush < maxdig && !b->contents [flush])
  139.     flush++;
  140.  
  141.   if (flush)
  142.     {
  143.       int i;
  144.  
  145.       b->after -= flush;
  146.       for (i = 0; i < LENGTH (b); i++)
  147.     b->contents[i] = b->contents[flush + i];
  148.     }
  149.  
  150. }
  151.  
  152. /* Return nonzero integer if the value of decimal number `b' is zero.  */
  153.  
  154. int
  155. decimal_zerop (b)
  156.      decimal b;
  157. {
  158.   return !LENGTH(b);
  159. }
  160.  
  161. /* Compare two decimal numbers arithmetically.
  162.    The value is < 0 if b1 < b2, > 0 if b1 > b2, 0 if b1 = b2.
  163.    This is the same way that `strcmp' reports the result of comparing
  164.    strings.  */ 
  165.  
  166. int
  167. decimal_compare (b1, b2)
  168.      decimal b1, b2;
  169. {
  170.   int l1, l2;
  171.   char *p1, *p2, *s1, *s2;
  172.   int i;
  173.  
  174.   /* If signs differ, deduce result from the signs */
  175.  
  176.   if (b2->sign && !b1->sign) return 1;
  177.   if (b1->sign && !b2->sign) return -1;
  178.  
  179.   /* If same sign but number of nonfraction digits differs,
  180.      the one with more of them is farther from zero.  */
  181.  
  182.   if (b1->before != b2->before)
  183.     if (b1->sign)
  184.       return (int) (b2->before - b1->before);
  185.     else
  186.       return (int) (b1->before - b2->before);
  187.  
  188.   /* Else compare the numbers digit by digit from high end */
  189.   l1 = LENGTH(b1);
  190.   l2 = LENGTH(b2);  
  191.   s1 = b1->contents;        /* Start of number -- don't back up digit pointer past here */
  192.   s2 = b2->contents;
  193.   p1 = b1->contents + l1;    /* Scanning pointer, for fetching digits.  */
  194.   p2 = b2->contents + l2;
  195.   for (i = MAX(l1, l2); i >= 0; i--)
  196.     {
  197.       int r = ((p1 != s1) ? *--p1 : 0) - ((p2 != s2) ? *--p2 : 0);
  198.       if (r)
  199.     return b1->sign ? -r : r;
  200.     }
  201.   return 0;
  202. }
  203.  
  204. /* Return the number of digits stored in decimal number `b' */
  205.  
  206. int
  207. decimal_length (b)
  208.      decimal b;
  209. {
  210.   return LENGTH(b);
  211. }
  212.  
  213. /* Return the number of fraction digits stored in decimal number `b'.  */
  214.  
  215. int
  216. decimal_after (b)
  217.      decimal b;
  218. {
  219.   return b->after;
  220. }
  221.  
  222. /* Round decimal number `b' to have only `digits' fraction digits.
  223.    Result is rounded to nearest unit in the last remaining digit.
  224.    Return the result, another decimal number.  */
  225.  
  226. decimal
  227. decimal_round_digits (b, digits)
  228.      decimal b;
  229.      int digits;
  230. {
  231.   decimal result;
  232.   int old;
  233.  
  234.   if (b->after <= digits) return decimal_copy (b);
  235.  
  236.   if (digits < 0)
  237.     {
  238.       decimal_error ("request to keep negative number of digits %d", digits);
  239.       return decimal_copy (b);
  240.     }
  241.  
  242.   result = make_decimal (b->before + 1, b->after);
  243.   result->sign = b->sign;
  244.   bcopy (b->contents, result->contents, LENGTH(b));
  245.  
  246.   old = result->after;
  247.  
  248.   /* Add .5 * last place to keep, so that we round rather than truncate */
  249.   /* Note this ignores sign of result, so if result is negative
  250.      it is subtracting */
  251.  
  252.   add_scaled (result, DECIMAL_HALF, 1, old - digits - 1);
  253.  
  254.   /* Flush desired digits, and any trailing zeros exposed by them.  */
  255.  
  256.   flush_trailing_digits (result, old - digits);
  257.  
  258.   /* Flush leading digits -- always is one, unless was a carry into it */
  259.  
  260.   while (result->before > 0
  261.      && result->contents[LENGTH(result) - 1] == 0)
  262.     result->before--;
  263.  
  264.   return result;
  265. }
  266.  
  267. /* Truncate decimal number `b' to have only `digits' fraction digits.
  268.    Any fraction digits in `b' beyond that are dropped and ignored.
  269.    Truncation is toward zero.
  270.    Return the result, another decimal number.  */
  271.  
  272. decimal
  273. decimal_trunc_digits (b, digits)
  274.      decimal b;
  275.      int digits;
  276. {
  277.   decimal result = decimal_copy (b);
  278.   int old = result->after;
  279.  
  280.   if (old <= digits) return result;
  281.  
  282.   if (digits < 0)
  283.     {
  284.       decimal_error ("request to keep negative number of digits %d", digits);
  285.       return result;
  286.     }
  287.  
  288.   flush_trailing_digits (result, old - digits);
  289.  
  290.   return result;
  291. }
  292.  
  293. /* Return the fractional part of decimal number `b':
  294.    that is, `b' - decimal_trunc_digits (`b') */
  295.  
  296. decimal
  297. decimal_fraction (b)
  298.      decimal b;
  299. {
  300.   decimal result = make_decimal (0, b->after);
  301.   bcopy (b->contents, result->contents, b->after);
  302.   return result;
  303. }
  304.  
  305. /* return an integ