1236 lines
29 KiB
C
1236 lines
29 KiB
C
|
/*
|
|||
|
* 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
|