aboutsummaryrefslogtreecommitdiff
path: root/gnu/usr.bin/dc/decimal.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/usr.bin/dc/decimal.c')
-rw-r--r--gnu/usr.bin/dc/decimal.c1235
1 files changed, 1235 insertions, 0 deletions
diff --git a/gnu/usr.bin/dc/decimal.c b/gnu/usr.bin/dc/decimal.c
new file mode 100644
index 000000000000..1fb95c18aa14
--- /dev/null
+++ b/gnu/usr.bin/dc/decimal.c
@@ -0,0 +1,1235 @@
+/*
+ * Arbitrary precision decimal arithmetic.
+ *
+ * Copyright (C) 1984 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can either send email to this
+ * program's author (see below) or write to: The Free Software Foundation,
+ * Inc.; 675 Mass Ave. Cambridge, MA 02139, USA.
+ */
+
+/* Some known problems:
+
+ Another problem with decimal_div is found when you try to
+ divide a number with > scale fraction digits by 1. The
+ expected result is simply truncation, but all sorts of things
+ happen instead. An example is that the result of .99999998/1
+ with scale set to 6 is .000001
+
+ There are some problems in the behavior of the decimal package
+ related to printing and parsing. The
+ printer is weird about very large output radices, tending to want
+ to output single ASCII characters for any and all digits (even
+ in radices > 127). The UNIX bc approach is to print digit groups
+ separated by spaces. There is a rather overwrought workaround in
+ the function decputc() in bcmisc.c, but it would be better if
+ decimal.c got a fix for this. */
+
+/* For stand-alone testing, compile with -DTEST.
+ This DTESTable feature defines a `main' function
+ which is a simple loop that accepts input of the form
+ number space op space number newline
+ where op is +, -, *, /, %, p or r,
+ and performs the operation and prints the operands and result.
+ `p' means print the first number in the radix spec'd by the second.
+ `r' means read the first one in the radix specified by the second
+ (and print the result in decimal).
+ Divide in this test keeps three fraction digits. */
+
+#include "decimal.h"
+
+#define MAX(a, b) (((a) > (b) ? (a) : (b)))
+
+/* Some constant decimal numbers */
+
+struct decimal decimal_zero = {0, 0, 0, 0, 0};
+
+struct decimal decimal_one = {0, 0, 1, 0, 1};
+
+/*** Assumes RADIX is even ***/
+struct decimal decimal_half = {0, 1, 0, 0, RADIX / 2};
+
+decimal static decimal_add1 (), decimal_sub1 ();
+static void add_scaled ();
+static int subtract_scaled ();
+
+/* Create and return a decimal number that has `before' digits before
+ the decimal point and `after' digits after. The digits themselves are
+ initialized to zero. */
+
+decimal
+make_decimal (before, after)
+ int before, after;
+{
+ decimal result;
+ if (before >= 1<<16)
+ {
+ decimal_error ("%d too many decimal digits", before);
+ return 0;
+ }
+ if (after >= 1<<15)
+ {
+ decimal_error ("%d too many decimal digits", after);
+ return 0;
+ }
+ result = (decimal) malloc (sizeof (struct decimal) + before + after - 1);
+ result->sign = 0;
+ result->before = before;
+ result->after = after;
+ result->refcnt = 0;
+ bzero (result->contents, before + after);
+ return result;
+}
+
+/* Create a copy of the decimal number `b' and return it. */
+
+decimal
+decimal_copy (b)
+ decimal b;
+{
+ decimal result = make_decimal (b->before, b->after);
+ bcopy (b->contents, result->contents, LENGTH(b));
+ result->sign = b->sign;
+ return result;
+}
+
+/* Copy a decimal number `b' but extend or truncate to exactly
+ `digits' fraction digits. */
+
+static decimal
+decimal_copy_1 (b, digits)
+ decimal b;
+ int digits;
+{
+ if (digits > b->after)
+ {
+ decimal result = make_decimal (b->before, digits);
+ bcopy (b->contents, result->contents + (digits - (int) b->after), LENGTH(b));
+ return result;
+ }
+ else
+ return decimal_round_digits (b, digits);
+}
+
+/* flush specified number `digits' of trailing fraction digits,
+ and flush any trailing fraction zero digits exposed after they are gone.
+ The number `b' is actually modified; no new storage is allocated.
+ That is why this is not global. */
+
+static void
+flush_trailing_digits (b, digits)
+ decimal b;
+ int digits;
+{
+ int flush = digits;
+ int maxdig = b->after;
+
+ while (flush < maxdig && !b->contents [flush])
+ flush++;
+
+ if (flush)
+ {
+ int i;
+
+ b->after -= flush;
+ for (i = 0; i < LENGTH (b); i++)
+ b->contents[i] = b->contents[flush + i];
+ }
+
+}
+
+/* Return nonzero integer if the value of decimal number `b' is zero. */
+
+int
+decimal_zerop (b)
+ decimal b;
+{
+ return !LENGTH(b);
+}
+
+/* Compare two decimal numbers arithmetically.
+ The value is < 0 if b1 < b2, > 0 if b1 > b2, 0 if b1 = b2.
+ This is the same way that `strcmp' reports the result of comparing
+ strings. */
+
+int
+decimal_compare (b1, b2)
+ decimal b1, b2;
+{
+ int l1, l2;
+ char *p1, *p2, *s1, *s2;
+ int i;
+
+ /* If signs differ, deduce result from the signs */
+
+ if (b2->sign && !b1->sign) return 1;
+ if (b1->sign && !b2->sign) return -1;
+
+ /* If same sign but number of nonfraction digits differs,
+ the one with more of them is farther from zero. */
+
+ if (b1->before != b2->before)
+ if (b1->sign)
+ return (int) (b2->before - b1->before);
+ else
+ return (int) (b1->before - b2->before);
+
+ /* Else compare the numbers digit by digit from high end */
+ l1 = LENGTH(b1);
+ l2 = LENGTH(b2);
+ s1 = b1->contents; /* Start of number -- don't back up digit pointer past here */
+ s2 = b2->contents;
+ p1 = b1->contents + l1; /* Scanning pointer, for fetching digits. */
+ p2 = b2->contents + l2;
+ for (i = MAX(l1, l2); i >= 0; i--)
+ {
+ int r = ((p1 != s1) ? *--p1 : 0) - ((p2 != s2) ? *--p2 : 0);
+ if (r)
+ return b1->sign ? -r : r;
+ }
+ return 0;
+}
+
+/* Return the number of digits stored in decimal number `b' */
+
+int
+decimal_length (b)
+ decimal b;
+{
+ return LENGTH(b);
+}
+
+/* Return the number of fraction digits stored in decimal number `b'. */
+
+int
+decimal_after (b)
+ decimal b;
+{
+ return b->after;
+}
+
+/* Round decimal number `b' to have only `digits' fraction digits.
+ Result is rounded to nearest unit in the last remaining digit.
+ Return the result, another decimal number. */
+
+decimal
+decimal_round_digits (b, digits)
+ decimal b;
+ int digits;
+{
+ decimal result;
+ int old;
+
+ if (b->after <= digits) return decimal_copy (b);
+
+ if (digits < 0)
+ {
+ decimal_error ("request to keep negative number of digits %d", digits);
+ return decimal_copy (b);
+ }
+
+ result = make_decimal (b->before + 1, b->after);
+ result->sign = b->sign;
+ bcopy (b->contents, result->contents, LENGTH(b));
+
+ old = result->after;
+
+ /* Add .5 * last place to keep, so that we round rather than truncate */
+ /* Note this ignores sign of result, so if result is negative
+ it is subtracting */
+
+ add_scaled (result, DECIMAL_HALF, 1, old - digits - 1);
+
+ /* Flush desired digits, and any trailing zeros exposed by them. */
+
+ flush_trailing_digits (result, old - digits);
+
+ /* Flush leading digits -- always is one, unless was a carry into it */
+
+ while (result->before > 0
+ && result->contents[LENGTH(result) - 1] == 0)
+ result->before--;
+
+ return result;
+}
+
+/* Truncate decimal number `b' to have only `digits' fraction digits.
+ Any fraction digits in `b' beyond that are dropped and ignored.
+ Truncation is toward zero.
+ Return the result, another decimal number. */
+
+decimal
+decimal_trunc_digits (b, digits)
+ decimal b;
+ int digits;
+{
+ decimal result = decimal_copy (b);
+ int old = result->after;
+
+ if (old <= digits) return result;
+
+ if (digits < 0)
+ {
+ decimal_error ("request to keep negative number of digits %d", digits);
+ return result;
+ }
+
+ flush_trailing_digits (result, old - digits);
+
+ return result;
+}
+
+/* Return the fractional part of decimal number `b':
+ that is, `b' - decimal_trunc_digits (`b') */
+
+decimal
+decimal_fraction (b)
+ decimal b;
+{
+ decimal result = make_decimal (0, b->after);
+ bcopy (b->contents, result->contents, b->after);
+ return result;
+}
+
+/* return an integer whose value is that of decimal `b', sans its fraction. */
+
+int
+decimal_to_int (b)
+ decimal b;
+{
+ int result = 0;
+ int i;
+ int end = b->after;
+
+ for (i = LENGTH(b) - 1; i >= end; i--)
+ {
+ result *= RADIX;
+ result += b->contents[i];
+ }
+ return result;
+}
+
+/* return a decimal whose value is the integer i. */
+
+decimal
+decimal_from_int (i)
+ int i;
+{
+ int log, tem;
+ decimal result;
+
+ for (log = 0, tem = (i > 0 ? i : - i); tem; log++, tem /= RADIX);
+
+ result = make_decimal (log, 0);
+
+ for (log = 0, tem = (i > 0 ? i : - i); tem; log++, tem /= RADIX)
+ result->contents[log] = tem % RADIX;
+
+ if (i < 0) result->sign = 1;
+ return result;
+}
+
+/* Return (as an integer) the result of dividing decimal number `b' by
+ integer `divisor'.
+ This is used in printing decimal numbers in other radices. */
+
+int
+decimal_int_rem (b, divisor)
+ decimal b;
+ int divisor;
+{
+ int len = LENGTH(b);
+ int end = b->after;
+ int accum = 0;
+ int i;
+
+ for (i = len - 1; i >= end; i--)
+ {
+ accum %= divisor;
+ accum *= RADIX;
+ accum += b->contents[i];
+ }
+ return accum % divisor;
+}
+
+/* Convert digit `digit' to a character and output it by calling
+ `charout' with it as arg. */
+
+static void
+print_digit (digit, charout)
+ int digit;
+ void (*charout) ();
+{
+ if (digit < 10)
+ charout ('0' + digit);
+ else
+ charout ('A' + digit - 10);
+}
+
+/* print decimal number `b' in radix `radix', assuming it is an integer.
+ `r' is `radix' expressed as a decimal number. */
+
+static
+decimal_print_1 (b, r, radix, charout)
+ decimal b, r;
+ int radix;
+ void (*charout) ();
+{
+ int digit = decimal_int_rem (b, radix);
+ decimal rest = decimal_div (b, r, 0);
+
+ if (!decimal_zerop (rest))
+ decimal_print_1 (rest, r, radix, charout);
+
+ print_digit (digit, charout);
+
+ free (rest);
+}
+
+/* User entry: print decimal number `b' in radix `radix' (an integer),
+ outputting characters by calling `charout'. */
+
+void
+decimal_print (b, charout, radix)
+ decimal b;
+ void (*charout) ();
+ int radix;
+{
+ if (b->sign) charout ('-');
+
+ if (radix == RADIX)
+ {
+ /* decimal output => just print the digits, inserting a point in
+ the proper place. */
+ int i;
+ int before = b->before;
+ int len = before + b->after;
+ for (i = 0; i < len; i++)
+ {
+ if (i == before) charout ('.');
+ /* Broken if RADIX /= 10
+ charout ('0' + b->contents [len - 1 - i]); */
+ print_digit (b->contents [len - 1 - i], charout);
+ }
+ if (!len)
+ charout ('0');
+ }
+ else
+ {
+ /* nonstandard radix: must use multiply and divide to determine the
+ digits of the number in that radix. */
+
+ int i;
+ extern double log10 ();
+ /* Compute the number of fraction digits we want to have in the
+ new radix. They should contain the same amount of
+ information as the decimal digits we have. */
+ int nfrac = (b->after / log10 ((double) radix) + .99);
+ decimal r = decimal_from_int (radix);
+ decimal intpart = decimal_trunc_digits (b, 0);
+
+ /* print integer part */
+ decimal_print_1 (intpart, r, radix, charout);
+ free (intpart);
+
+ /* print fraction part */
+ if (nfrac)
+ {
+ decimal tem1, tem2;
+ tem1 = decimal_fraction (b);
+ charout ('.');
+ /* repeatedly multiply by `radix', print integer part as one digit,
+ and flush the integer part. */
+ for (i = 0; i < nfrac; i++)
+ {
+ tem2 = decimal_mul (tem1, r);
+ free (tem1);
+ print_digit (decimal_to_int (tem2), charout);
+ tem1 = decimal_fraction (tem2);
+ free (tem2);
+ }
+ free (tem1);
+ }
+ free (r);
+ }
+}
+
+static int
+decode_digit (digitchar)
+ char digitchar;
+{
+ if ('0' <= digitchar && digitchar <= '9')
+ return digitchar - '0';
+ if ('a' <= digitchar && digitchar <= 'z')
+ return digitchar - 'a' + 10;
+ if ('A' <= digitchar && digitchar <= 'Z')
+ return digitchar - 'A' + 10;
+ return -1;
+}
+
+/* Parse string `s' into a number using radix `radix'
+ and return result as a decimal number. */
+
+decimal
+decimal_parse (s, radix)
+ char *s;
+ int radix;
+{
+ int i, len, before = -1;
+ char *p;
+ char c;
+ decimal result;
+ int negative = 0;
+ int excess_digit = 0;
+
+ if (*s == '-')
+ {
+ s++;
+ negative = 1;
+ }
+
+ /* First scan for valid characters.
+ Count total num digits, and count num before the decimal point. */
+
+ p = s;
+ i = 0;
+ while (c = *p++)
+ {
+ if (c == '.')
+ {
+ if (before >= 0)
+ decimal_error ("two decimal points in %s", s);
+ before = i;
+ }
+ else if (c == '0' && !i && before < 0)
+ s++; /* Discard leading zeros */
+ else if (decode_digit (c) >= 0)
+ {
+ i++;
+ if (decode_digit (c) > RADIX)
+ excess_digit = 1;
+ }
+ else
+ decimal_error ("invalid number %s", s);
+ }
+
+ len = i;
+ if (before < 0) before = i;
+
+ p = s;
+
+ /* Now parse those digits */
+
+ if (radix != RADIX || excess_digit)
+ {
+ decimal r = decimal_from_int (radix);
+ extern double log10 ();
+ int digits = (len - before) * log10 ((double) radix) + .99;
+ result = decimal_copy (DECIMAL_ZERO);
+
+ /* Parse all the digits into an integer, ignoring decimal point,
+ by multiplying by `radix'. */
+
+ while (i > 0 && (c = *p++))
+ {
+ if (c != '.')
+ {
+ decimal newdig = decimal_from_int (decode_digit (c));
+ decimal prod = decimal_mul (result, r);
+ decimal newresult = decimal_add (newdig, prod);
+
+ free (newdig); free (prod); free (result);
+ result = newresult;
+ i--;
+ }
+ }
+
+ /* Now put decimal point in right place
+ by dividing by `radix' once for each digit
+ that really should have followed the decimal point. */
+
+ for (i = before; i < len; i++)
+ {
+ decimal newresult = decimal_div (result, r, digits);
+ free (result);
+ result = newresult;
+ }
+ free (r);
+ }
+ else
+ {
+ /* radix is standard - just copy the digits into a decimal number. */
+
+ int tem;
+ result = make_decimal (before, len - before);
+
+ while (i > 0 && (c = *p++))
+ {
+ if ((c != '.') &&
+ ((tem = decode_digit (c)) >= 0))
+ result->contents [--i] = tem;
+ }
+ }
+
+ if (negative) result->sign = 1;
+ flush_trailing_digits (result, 0);
+ return result;
+}
+
+/* Add b1 and b2, considering their signs */
+
+decimal
+decimal_add (b1, b2)
+ decimal b1, b2;
+{
+ decimal v;
+
+ if (b1->sign != b2->sign)
+ v = decimal_sub1 (b1, b2);
+ else
+ v = decimal_add1 (b1, b2);
+ if (b1->sign && !decimal_zerop (v))
+ v->sign = !v->sign;
+ return v;
+}
+
+/* Add b1 and minus b2, considering their signs */
+
+decimal
+decimal_sub (b1, b2)
+ decimal b1, b2;
+{
+ decimal v;
+
+ if (b1->sign != b2->sign)
+ v = decimal_add1 (b1, b2);
+ else
+ v = decimal_sub1 (b1, b2);
+ if (b1->sign && !decimal_zerop (v))
+ v->sign = !v->sign;
+ return v;
+}
+
+/* Return the negation of b2. */
+
+decimal
+decimal_neg (b2)
+ decimal b2;
+{
+ decimal v = decimal_copy (b2);
+
+ if (!decimal_zerop (v))
+ v->sign = !v->sign;
+ return v;
+}
+
+/* add magnitudes of b1 and b2, ignoring their signs. */
+
+static decimal
+decimal_add1 (b1, b2)
+ decimal b1, b2;
+{
+ int before = MAX (b1->before, b2->before);
+ int after = MAX (b1->after, b2->after);
+
+ int len = before+after+1;
+ decimal result = make_decimal (before+1, after);
+
+ int i;
+ char *s1 = b1->contents;
+ char *s2 = b2->contents;
+ char *p1 = s1 + b1->after - after;
+ char *p2 = s2 + b2->after - after;
+ char *e1 = s1 + b1->before + b1->after;
+ char *e2 = s2 + b2->before + b2->after;
+ char *pr = result->contents;
+ int accum = 0;
+
+ for (i = 0; i < len; i++, p1++, p2++)
+ {
+ accum /= RADIX;
+ if (p1 >= s1 && p1 < e1) accum += *p1;
+ if (p2 >= s2 && p2 < e2) accum += *p2;
+ *pr++ = accum % RADIX;
+ }
+ if (!accum)
+ (result->before)--;
+
+ flush_trailing_digits (result, 0);
+
+ return result;
+}
+
+/* subtract magnitude of b2 from that or b1, returning signed decimal
+ number. */
+
+static decimal
+decimal_sub1 (b1, b2)
+ decimal b1, b2;
+{
+ int before = MAX (b1->before, b2->before);
+ int after = MAX (b1->after, b2->after);
+
+ int len = before+after;
+ decimal result = make_decimal (before, after);
+
+ int i;
+ char *s1 = b1->contents;
+ char *s2 = b2->contents;
+ char *p1 = s1 + b1->after - after;
+ char *p2 = s2 + b2->after - after;
+ char *e1 = s1 + b1->before + b1->after;
+ char *e2 = s2 + b2->before + b2->after;
+ char *pr = result->contents;
+ int accum = 0;
+
+ for (i = 0; i < len; i++, p1++, p2++)
+ {
+ if (p1 >= s1 && p1 < e1) accum += *p1;
+ if (p2 >= s2 && p2 < e2) accum -= *p2;
+ if (accum < 0 && accum % RADIX)
+ *pr = RADIX - (- accum) % RADIX;
+ else
+ *pr = accum % RADIX;
+ accum -= *pr++;
+ accum /= RADIX;
+ }
+
+ /* If result is negative, subtract it from RADIX**length
+ so that we get the right digits for sign-magnitude
+ rather than RADIX-complement */
+
+ if (accum)
+ {
+ result->sign = 1;
+ pr = result->contents;
+ accum = 0;
+ for (i = 0; i < len; i++)
+ {
+ accum -= *pr;
+ if (accum)
+ *pr = accum + RADIX;
+ else
+ *pr = 0;
+ accum -= *pr++;
+ accum /= RADIX;
+ }
+ }
+
+ /* flush leading nonfraction zero digits */
+
+ while (result->before && *--pr == 0)
+ (result->before)--;
+
+ flush_trailing_digits (result, 0);
+
+ return result;
+}
+
+/* multiply b1 and b2 keeping `digits' fraction digits */
+
+decimal
+decimal_mul_rounded (b1, b2, digits)
+ decimal b1, b2;
+ int digits;
+{
+ decimal tem = decimal_mul (b1, b2);
+ decimal result = decimal_round_digits (tem, digits);
+ free (tem);
+ return result;
+}
+
+/* multiply b1 and b2 keeping the right number of fraction digits
+ for the `dc' program with precision = `digits'. */
+
+decimal
+decimal_mul_dc (b1, b2, digits)
+ decimal b1, b2;
+ int digits;
+{
+ decimal tem = decimal_mul (b1, b2);
+ decimal result
+ = decimal_round_digits (tem, MAX (digits, MAX (b1->after, b2->after)));
+ free (tem);
+ return result;
+}
+
+/* multiply b1 and b2 as decimal error-free values;
+ keep LENGTH(b1) plus LENGTH(b2) significant figures. */
+
+decimal
+decimal_mul (b1, b2)
+ decimal b1, b2;
+{
+ decimal result = make_decimal (b1->before + b2->before, b1->after + b2->after);
+ int i;
+ int length2 = LENGTH(b2);
+ char *pr;
+
+ for (i = 0; i < length2; i++)
+ add_scaled (result, b1, b2->contents[i], i);
+
+ /* flush leading nonfraction zero digits */
+
+ pr = result->contents + LENGTH(result);
+ while (result->before && *--pr == 0)
+ (result->before)--;
+
+ flush_trailing_digits (result, 0); /* flush trailing zeros */
+
+ /* Set sign properly */
+
+ if (b1->sign != b2->sign && LENGTH(result))
+ result->sign = 1;
+
+ return result;
+}
+
+/* Modify decimal number `into' by adding `from',
+ multiplied by `factor' (which should be nonnegative and less than RADIX)
+ and shifted left `scale' digits at the least significant end. */
+
+static void
+add_scaled (into, from, factor, scale)
+ decimal into, from;
+ int factor, scale;
+{
+ char *pf = from->contents;
+ char *pi = into->contents + scale;
+ int lengthf = LENGTH(from);
+ int lengthi = LENGTH(into) - scale;
+
+ int accum = 0;
+ int i;
+
+ for (i = 0; i < lengthi; i++)
+ {
+ accum /= RADIX;
+ if (i < lengthf)
+ accum += *pf++ * factor;
+ accum += *pi;
+ *pi++ = accum % RADIX;
+ }
+}
+
+/* Divide decimal number `b1' by `b2', keeping at most `digits'
+ fraction digits.
+ Returns the result as a decimal number.
+
+ When division is not exact, the quotient is truncated toward zero. */
+
+decimal
+decimal_div (b1, b2, digits)
+ decimal b1, b2;
+ int digits;
+{
+ decimal result = make_decimal (MAX(1, (int) (1 + b1->before - b2->before)), digits);
+
+ /* b1copy holds what is left of the dividend,
+ that is not accounted for by the quotient digits already known */
+
+ decimal b1copy = decimal_copy_1 (b1, b2->after + digits);
+ int length1 = LENGTH(b1copy);
+ int length2 = LENGTH(b2);
+ int lengthr = LENGTH(result);
+ int i;
+
+ /* leading_divisor_digits contains the first two divisor digits, as
+ an integer */
+
+ int leading_divisor_digits = b2->contents[length2-1]*RADIX;
+ if (length2 > 1)
+ leading_divisor_digits += b2->contents[length2-2];
+
+ if (decimal_zerop (b2))
+ {
+ decimal_error ("divisor is zero", 0);
+ return decimal_copy (DECIMAL_ZERO);
+ }
+
+ if (lengthr <= (length1 - length2))
+ abort(); /* My reasoning says this cannot happen, I hope */
+
+ for (i = length1 - length2; i >= 0; i--)
+ {
+ /* Guess the next quotient digit (in order of decreasing significance)
+ using integer division */
+
+ int guess;
+ int trial_dividend = b1copy->contents[length2+i-1]*RADIX;
+ if (i != length1 - length2)
+ trial_dividend += b1copy->contents[length2+i]*RADIX*RADIX;
+ if (length2 + i > 1)
+ trial_dividend += b1copy->contents[length2+i-2];
+
+ guess = trial_dividend / leading_divisor_digits;
+
+ /* Remove the quotient times this digit from the dividend left */
+ /* We may find that the quotient digit is too large,
+ when we consider the entire divisor.
+ Then we decrement the quotient digit and add the divisor back in */
+
+ if (guess && 0 > subtract_scaled (b1copy, b2, guess, i))
+ {
+ guess--;
+ add_scaled (b1copy, b2, 1, i);
+ }
+
+ if (guess >= RADIX)
+ {
+ result->contents[i + 1] += guess / RADIX;
+ guess %= RADIX;
+ }
+ result->contents[i] = guess;
+ }
+
+ free (b1copy);
+
+ result->sign = (b1->sign != b2->sign);
+
+ /* flush leading nonfraction zero digits */
+
+ {
+ char *pr = result->contents + lengthr;
+ while (result->before && *--pr == 0)
+ (result->before)--;
+ }
+
+ flush_trailing_digits (result, 0); /* Flush trailing zero fraction digits */
+
+ return result;
+}
+
+/* The remainder for the above division.
+ Same as `b1' - (`b1' / `b2') * 'b2'.
+ Note that the value depends on the number of fraction digits
+ that were kept in computing `b1' / `b2';
+ the argument `digits' specifies this.
+
+ The remainder has the same sign as the dividend.
+ The divisor's sign is ignored. */
+
+decimal
+decimal_rem (b1, b2, digits)
+ decimal b1, b2;
+ int digits;
+{
+ decimal b1copy = decimal_copy_1 (b1, b2->after + digits);
+ int length1 = LENGTH(b1copy);
+ int length2 = LENGTH(b2);
+ int i;
+
+ int leading_divisor_digits = b2->contents[length2-1]*RADIX;
+
+ if (length2 > 1)
+ leading_divisor_digits += b2->contents[length2-2];
+
+ if (decimal_zerop (b2))
+ {
+ decimal_error ("divisor is zero", 0);
+ return decimal_copy (DECIMAL_ZERO);
+ }
+
+ /* Do like division, above, but throw away the quotient.
+ Keep only the final `rest of dividend', which becomes the remainder. */
+
+ for (i = length1 - length2; i >= 0; i--)
+ {
+ int guess;
+ int trial_dividend = b1copy->contents[length2+i-1]*RADIX;
+ if (i != length1 - length2)
+ trial_dividend += b1copy->contents[length2+i]*RADIX*RADIX;
+ if (length2 + i > 1)
+ trial_dividend += b1copy->contents[length2+i-2];
+
+ guess = trial_dividend / leading_divisor_digits;
+
+ if (guess && 0 > subtract_scaled (b1copy, b2, guess, i))
+ {
+ guess--;
+ add_scaled (b1copy, b2, 1, i);
+ }
+ /* No need to check whether guess exceeds RADIX
+ since we are not saving guess. */
+ }
+
+ /* flush leading nonfraction zero digits */
+
+ {
+ char *pr = b1copy->contents + length1;
+ while (b1copy->before && *--pr == 0)
+ (b1copy->before)--;
+ }
+
+ flush_trailing_digits (b1copy, 0);
+ return b1copy;
+}
+
+/* returns negative number if we chose factor too large */
+
+static int
+subtract_scaled (into, from, factor, scale)
+ decimal into, from;
+ int factor, scale;
+{
+ char *pf = from->contents;
+ char *pi = into->contents + scale;
+ int lengthf = LENGTH(from);
+ int lengthi = LENGTH(into) - scale;
+ int accum = 0;
+ int i;
+
+ for (i = 0; i < lengthi && i <= lengthf; i++)
+ {
+ if (i < lengthf)
+ accum -= *pf++ * factor;
+ accum += *pi;
+ if (accum < 0 && accum % RADIX)
+ *pi = RADIX - (- accum) % RADIX;
+ else
+ *pi = accum % RADIX;
+ accum -= *pi++;
+ accum /= RADIX;
+ }
+ return accum;
+}
+
+/* Return the square root of decimal number D, using Newton's method.
+ Number of fraction digits returned is max of FRAC_DIGITS
+ and D's number of fraction digits. */
+
+decimal
+decimal_sqrt (d, frac_digits)
+ decimal d;
+ int frac_digits;
+{
+ decimal guess;
+ int notdone = 1;
+
+ if (decimal_zerop (d)) return d;
+ if (d->sign)
+ {
+ decimal_error ("square root argument negative", 0);
+ return decimal_copy (DECIMAL_ZERO);
+ }
+
+ frac_digits = MAX (frac_digits, d->after);
+
+ /* Compute an initial guess by taking the square root
+ of a nearby power of RADIX. */
+
+ if (d->before)
+ {
+ guess = make_decimal ((d->before + 1) / 2, 0);
+ guess->contents[guess->before - 1] = 1;
+ }
+ else
+ {
+ /* Arg is less than 1; compute nearest power of RADIX */
+ char *p = d->contents + LENGTH(d);
+ char *sp = p;
+
+ while (!*--p); /* Find most significant nonzero digit */
+ if (sp - p == 1)
+ {
+ /* Arg is bigger than 1/RADIX; use 1 as a guess */
+ guess = decimal_copy (DECIMAL_ONE);
+ }
+ else
+ {
+ guess = make_decimal (0, (sp - p) / 2);
+ guess->contents[0] = 1;
+ }
+ }
+
+ /* Iterate doing guess = (guess + d/guess) / 2 */
+
+ while (notdone)
+ {
+ decimal tem1 = decimal_div (d, guess, frac_digits + 1);
+ decimal tem2 = decimal_add (guess, tem1);
+ decimal tem3 = decimal_mul_rounded (tem2, DECIMAL_HALF, frac_digits);
+ notdone = decimal_compare (guess, tem3);
+ free (tem1);
+ free (tem2);
+ free (guess);
+ guess = tem3;
+ if (decimal_zerop (guess)) return guess; /* Avoid divide-by-zero */
+ }
+
+ return guess;
+}
+
+/* Raise decimal number `base' to power of integer part of decimal
+ number `expt'.
+ This function depends on using radix 10.
+ It is too hard to write it to work for any value of RADIX,
+ so instead it is simply not available if RADIX is not ten. */
+
+#if !(RADIX - 10)
+
+decimal
+decimal_expt (base, expt, frac_digits)
+ decimal base, expt;
+ int frac_digits;
+{
+ decimal accum = decimal_copy (DECIMAL_ONE);
+ decimal basis1 = base;
+ int digits = expt->before;
+ int dig = 0; /* Expt digit being processed */
+
+ if (expt->sign)
+ /* If negative power, take reciprocal first thing
+ so that fraction digit truncation won't destroy
+ what will ultimately be nonfraction digits. */
+ basis1 = decimal_div (DECIMAL_ONE, base, frac_digits);
+ while (dig < digits)
+ {
+ decimal basis2, basis4, basis8, basis10;
+ int thisdigit = expt->contents[expt->after + dig];
+
+ /* Compute factors to multiply in for each bit of this digit */
+
+ basis2 = decimal_mul_rounded (basis1, basis1, frac_digits);
+ basis4 = decimal_mul_rounded (basis2, basis2, frac_digits);
+ basis8 = decimal_mul_rounded (basis4, basis4, frac_digits);
+
+ /* Now accumulate the factors this digit value selects */
+
+ if (thisdigit & 1)
+ {
+ decimal accum1 = decimal_mul_rounded (accum, basis1, frac_digits);
+ free (accum);
+ accum = accum1;
+ }
+
+ if (thisdigit & 2)
+ {
+ decimal accum1 = decimal_mul_rounded (accum, basis2, frac_digits);
+ free (accum);
+ accum = accum1;
+ }
+
+ if (thisdigit & 4)
+ {
+ decimal accum1 = decimal_mul_rounded (accum, basis4, frac_digits);
+ free (accum);
+ accum = accum1;
+ }
+
+ if (thisdigit & 8)
+ {
+ decimal accum1 = decimal_mul_rounded (accum, basis8, frac_digits);
+ free (accum);
+ accum = accum1;
+ }
+
+ /* If there are further digits, compute the basis1 for the next digit */
+
+ if (++dig < digits)
+ basis10 = decimal_mul_rounded (basis2, basis8, frac_digits);
+
+ /* Free intermediate results */
+
+ if (basis1 != base) free (basis1);
+ free (basis2);
+ free (basis4);
+ free (basis8);
+ basis1 = basis10;
+ }
+ return accum;
+}
+#endif
+
+#ifdef TEST
+
+fputchar (c)
+ char c;
+{
+ putchar (c);
+}
+
+/* Top level that can be used to test the arithmetic functions */
+
+main ()
+{
+ char s1[40], s2[40];
+ decimal b1, b2, b3;
+ char c;
+
+ while (1)
+ {
+ scanf ("%s %c %s", s1, &c, s2);
+ b1 = decimal_parse (s1, RADIX);
+ b2 = decimal_parse (s2, RADIX);
+ switch (c)
+ {
+ default:
+ c = '+';
+ case '+':
+ b3 = decimal_add (b1, b2);
+ break;
+ case '*':
+ b3 = decimal_mul (b1, b2);
+ break;
+ case '/':
+ b3 = decimal_div (b1, b2, 3);
+ break;
+ case '%':
+ b3 = decimal_rem (b1, b2, 3);
+ break;
+ case 'p':
+ decimal_print (b1, fputchar, RADIX);
+ printf (" printed in base %d is ", decimal_to_int (b2));
+ decimal_print (b1, fputchar, decimal_to_int (b2));
+ printf ("\n");
+ continue;
+ case 'r':
+ printf ("%s read in base %d is ", s1, decimal_to_int (b2));
+ decimal_print (decimal_parse (s1, decimal_to_int (b2)), fputchar, RADIX);
+ printf ("\n");
+ continue;
+ }
+ decimal_print (b1, fputchar, RADIX);
+ printf (" %c ", c);
+ decimal_print (b2, fputchar, RADIX);
+ printf (" = ");
+ decimal_print (b3, fputchar, RADIX);
+ printf ("\n");
+ }
+}
+
+decimal_error (s1, s2)
+ char *s1, *s2;
+{
+ printf ("\n");
+ printf (s1, s2);
+ printf ("\n");
+}
+
+static void
+pbi (b)
+ int b;
+{
+ decimal_print ((decimal) b, fputchar, RADIX);
+}
+
+static void
+pb (b)
+ decimal b;
+{
+ decimal_print (b, fputchar, RADIX);
+}
+
+#endif