home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
LIBSRC.ZOO
/
libsrc
/
longlong
/
bdiv.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-02-22
|
4KB
|
186 lines
#include "longlong.h"
static int bshift ();
/* Divide a by b, producing quotient q and remainder r.
sizeof a is m
sizeof b is n
sizeof q is m - n
sizeof r is n
The quotient must fit in m - n bytes, i.e., the most significant
n digits of a must be less than b, and m must be greater than n. */
/* The name of this used to be __div_internal,
but that is too long for SYSV. */
void
__bdiv (a, b, q, r, m, n)
unsigned short *a, *b, *q, *r;
size_t m, n;
{
unsigned long qhat, rhat;
unsigned long acc;
unsigned short *u = (unsigned short *) alloca (m);
unsigned short *v = (unsigned short *) alloca (n);
unsigned short *u0, *u1, *u2;
unsigned short *v0;
int d, qn;
int i, j;
m /= sizeof *a;
n /= sizeof *b;
qn = m - n;
/* Remove leading zero digits from divisor, and the same number of
digits (which must be zero) from dividend. */
while (b[big_end (n)] == 0)
{
r[big_end (n)] = 0;
a += little_end (2);
b += little_end (2);
r += little_end (2);
m--;
n--;
/* Check for zero divisor. */
if (n == 0)
abort ();
}
/* If divisor is a single digit, do short division. */
if (n == 1)
{
acc = a[big_end (m)];
a += little_end (2);
for (j = big_end (qn); is_not_lsd (j, qn); j = next_lsd (j))
{
acc = (acc << 16) | a[j];
q[j] = acc / *b;
acc = acc % *b;
}
*r = acc;
return;
}
/* No such luck, must do long division. Shift divisor and dividend
left until the high bit of the divisor is 1. */
for (d = 0; d < 16; d++)
if (b[big_end (n)] & (1 << (16 - 1 - d)))
break;
bshift (a, d, u, 0, m);
bshift (b, d, v, 0, n);
/* Get pointers to the high dividend and divisor digits. */
u0 = u + big_end (m) - big_end (qn);
u1 = next_lsd (u0);
u2 = next_lsd (u1);
u += little_end (2);
v0 = v + big_end (n);
/* Main loop: find a quotient digit, multiply it by the divisor,
and subtract that from the dividend, shifted over the right amount. */
for (j = big_end (qn); is_not_lsd (j, qn); j = next_lsd (j))
{
/* Quotient digit initial guess: high 2 dividend digits over high
divisor digit. */
if (u0[j] == *v0)
{
qhat = B - 1;
rhat = (unsigned long) *v0 + u1[j];
}
else
{
unsigned long numerator = ((unsigned long) u0[j] << 16) | u1[j];
qhat = numerator / *v0;
rhat = numerator % *v0;
}
/* Now get the quotient right for high 3 dividend digits over
high 2 divisor digits. */
while (rhat < B && qhat * *next_lsd (v0) > ((rhat << 16) | u2[j]))
{
qhat -= 1;
rhat += *v0;
}
/* Multiply quotient by divisor, subtract from dividend. */
acc = 0;
for (i = little_end (n); is_not_msd (i, n); i = next_msd (i))
{
acc += (unsigned long) (u + j)[i] - v[i] * qhat;
(u + j)[i] = acc & low16;
if (acc < B)
acc = 0;
else
acc = (acc >> 16) | -B;
}
q[j] = qhat;
/* Quotient may have been too high by 1. If dividend went negative,
decrement the quotient by 1 and add the divisor back. */
if ((signed long) (acc + u0[j]) < 0)
{
q[j] -= 1;
acc = 0;
for (i = little_end (n); is_not_msd (i, n); i = next_msd (i))
{
acc += (unsigned long) (u + j)[i] + v[i];
(u + j)[i] = acc & low16;
acc = acc >> 16;
}
}
}
/* Now the remainder is what's left of the dividend, shifted right
by the amount of the normalizing left shift at the top. */
r[big_end (n)] = bshift (u + 1 + little_end (j - 1),
16 - d,
r + little_end (2),
u[little_end (m - 1)] >> d,
n - 1);
}
/* Left shift U by K giving W; fill the introduced low-order bits with
CARRY_IN. Length of U and W is N. Return carry out. K must be
in 0 .. 16. */
static int
bshift (u, k, w, carry_in, n)
unsigned short *u, *w, carry_in;
int k, n;
{
unsigned long acc;
int i;
if (k == 0)
{
bcopy (u, w, n * sizeof *u);
return 0;
}
acc = carry_in;
for (i = little_end (n); is_not_msd (i, n); i = next_msd (i))
{
acc |= (unsigned long) u[i] << k;
w[i] = acc & low16;
acc = acc >> 16;
}
return acc;
}