xref: /csrg-svn/lib/libc/quad/qdivrem.c (revision 53459)
153430Sbostic /*-
253430Sbostic  * Copyright (c) 1992 The Regents of the University of California.
353430Sbostic  * All rights reserved.
453430Sbostic  *
553430Sbostic  * %sccs.include.redist.c%
653430Sbostic  */
753430Sbostic 
853430Sbostic #if defined(LIBC_SCCS) && !defined(lint)
9*53459Sbostic static char sccsid[] = "@(#)qdivrem.c	5.5 (Berkeley) 05/12/92";
1053430Sbostic #endif /* LIBC_SCCS and not lint */
1153430Sbostic 
12*53459Sbostic /* Copyright (C) 1989, 1992 Free Software Foundation, Inc.
13*53459Sbostic 
14*53459Sbostic This file is part of GNU CC.
15*53459Sbostic 
16*53459Sbostic GNU CC is free software; you can redistribute it and/or modify
17*53459Sbostic it under the terms of the GNU General Public License as published by
18*53459Sbostic the Free Software Foundation; either version 2, or (at your option)
19*53459Sbostic any later version.
20*53459Sbostic 
21*53459Sbostic GNU CC is distributed in the hope that it will be useful,
22*53459Sbostic but WITHOUT ANY WARRANTY; without even the implied warranty of
23*53459Sbostic MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24*53459Sbostic GNU General Public License for more details.
25*53459Sbostic 
26*53459Sbostic You should have received a copy of the GNU General Public License
27*53459Sbostic along with GNU CC; see the file COPYING.  If not, write to
28*53459Sbostic the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
29*53459Sbostic 
30*53459Sbostic /* As a special exception, if you link this library with files
31*53459Sbostic    compiled with GCC to produce an executable, this does not cause
32*53459Sbostic    the resulting executable to be covered by the GNU General Public License.
33*53459Sbostic    This exception does not however invalidate any other reasons why
34*53459Sbostic    the executable file might be covered by the GNU General Public License.  */
35*53459Sbostic 
3653455Sbostic #include <unistd.h>
3753455Sbostic 
3851748Smckusick #include "longlong.h"
3951748Smckusick 
4051748Smckusick static int bshift ();
4151748Smckusick 
4251748Smckusick /* Divide a by b, producing quotient q and remainder r.
4351748Smckusick 
4451748Smckusick        sizeof a is m
4551748Smckusick        sizeof b is n
4651748Smckusick        sizeof q is m - n
4751748Smckusick        sizeof r is n
4851748Smckusick 
4951748Smckusick    The quotient must fit in m - n bytes, i.e., the most significant
5051748Smckusick    n digits of a must be less than b, and m must be greater than n.  */
5151748Smckusick 
5251748Smckusick /* The name of this used to be __div_internal,
5351748Smckusick    but that is too long for SYSV.  */
5451748Smckusick 
5551748Smckusick void
5651748Smckusick __bdiv (a, b, q, r, m, n)
5751748Smckusick      unsigned short *a, *b, *q, *r;
5853455Sbostic      unsigned long m, n;
5951748Smckusick {
6051748Smckusick   unsigned long qhat, rhat;
6151748Smckusick   unsigned long acc;
6253455Sbostic   unsigned short array[12];
6353455Sbostic   unsigned short *u, *v, *u0, *u1, *u2;
6451748Smckusick   unsigned short *v0;
6551748Smckusick   int d, qn;
6651748Smckusick   int i, j;
6751748Smckusick 
6853455Sbostic   if (m > 16 || n > 8) {
6953455Sbostic #ifdef KERNEL
7053455Sbostic 	panic("bdiv: out of space");
7153455Sbostic #else
7253455Sbostic #define	EMSG	"bdiv: out of space."
7353455Sbostic 	(void)write(STDERR_FILENO, EMSG, sizeof(EMSG) - 1);
7453455Sbostic 	abort();
7553455Sbostic #endif
7653455Sbostic   }
7753455Sbostic   u = &array[0];
7853455Sbostic   v = &array[m / sizeof(unsigned short)];
7951748Smckusick   m /= sizeof *a;
8051748Smckusick   n /= sizeof *b;
8151748Smckusick   qn = m - n;
8251748Smckusick 
8351748Smckusick   /* Remove leading zero digits from divisor, and the same number of
8451748Smckusick      digits (which must be zero) from dividend.  */
8551748Smckusick 
8651748Smckusick   while (b[big_end (n)] == 0)
8751748Smckusick     {
8851748Smckusick       r[big_end (n)] = 0;
8951748Smckusick 
9051748Smckusick       a += little_end (2);
9151748Smckusick       b += little_end (2);
9251748Smckusick       r += little_end (2);
9351748Smckusick       m--;
9451748Smckusick       n--;
9551748Smckusick 
9651748Smckusick       /* Check for zero divisor.  */
9751748Smckusick       if (n == 0)
9851750Smckusick       {
9951750Smckusick 	*r /= n;	/* force a divide-by-zero trap */
10051750Smckusick 	return;
10151750Smckusick       }
10251748Smckusick     }
10351748Smckusick 
10451748Smckusick   /* If divisor is a single digit, do short division.  */
10551748Smckusick 
10651748Smckusick   if (n == 1)
10751748Smckusick     {
10851748Smckusick       acc = a[big_end (m)];
10951748Smckusick       a += little_end (2);
11051748Smckusick       for (j = big_end (qn); is_not_lsd (j, qn); j = next_lsd (j))
11151748Smckusick 	{
11251748Smckusick 	  acc = (acc << 16) | a[j];
11351748Smckusick 	  q[j] = acc / *b;
11451748Smckusick 	  acc = acc % *b;
11551748Smckusick 	}
11651748Smckusick       *r = acc;
11751748Smckusick       return;
11851748Smckusick     }
11951748Smckusick 
12051748Smckusick   /* No such luck, must do long division. Shift divisor and dividend
12151748Smckusick      left until the high bit of the divisor is 1.  */
12251748Smckusick 
12351748Smckusick   for (d = 0; d < 16; d++)
12451748Smckusick     if (b[big_end (n)] & (1 << (16 - 1 - d)))
12551748Smckusick       break;
12651748Smckusick 
12751748Smckusick   bshift (a, d, u, 0, m);
12851748Smckusick   bshift (b, d, v, 0, n);
12951748Smckusick 
13051748Smckusick   /* Get pointers to the high dividend and divisor digits.  */
13151748Smckusick 
13251748Smckusick   u0 = u + big_end (m) - big_end (qn);
13351748Smckusick   u1 = next_lsd (u0);
13451748Smckusick   u2 = next_lsd (u1);
13551748Smckusick   u += little_end (2);
13651748Smckusick 
13751748Smckusick   v0 = v + big_end (n);
13851748Smckusick 
13951748Smckusick   /* Main loop: find a quotient digit, multiply it by the divisor,
14051748Smckusick      and subtract that from the dividend, shifted over the right amount. */
14151748Smckusick 
14251748Smckusick   for (j = big_end (qn); is_not_lsd (j, qn); j = next_lsd (j))
14351748Smckusick     {
14451748Smckusick       /* Quotient digit initial guess: high 2 dividend digits over high
14551748Smckusick 	 divisor digit.  */
14651748Smckusick 
14751748Smckusick       if (u0[j] == *v0)
14851748Smckusick 	{
14951748Smckusick 	  qhat = B - 1;
15051748Smckusick 	  rhat = (unsigned long) *v0 + u1[j];
15151748Smckusick 	}
15251748Smckusick       else
15351748Smckusick 	{
15451748Smckusick 	  unsigned long numerator = ((unsigned long) u0[j] << 16) | u1[j];
15551748Smckusick 	  qhat = numerator / *v0;
15651748Smckusick 	  rhat = numerator % *v0;
15751748Smckusick 	}
15851748Smckusick 
15951748Smckusick       /* Now get the quotient right for high 3 dividend digits over
16051748Smckusick 	 high 2 divisor digits.  */
16151748Smckusick 
16251748Smckusick       while (rhat < B && qhat * *next_lsd (v0) > ((rhat << 16) | u2[j]))
16351748Smckusick 	{
16451748Smckusick 	  qhat -= 1;
16551748Smckusick 	  rhat += *v0;
16651748Smckusick 	}
16751748Smckusick 
16851748Smckusick       /* Multiply quotient by divisor, subtract from dividend.  */
16951748Smckusick 
17051748Smckusick       acc = 0;
17151748Smckusick       for (i = little_end (n); is_not_msd (i, n); i = next_msd (i))
17251748Smckusick 	{
17351748Smckusick 	  acc += (unsigned long) (u + j)[i] - v[i] * qhat;
17451748Smckusick 	  (u + j)[i] = acc & low16;
17551748Smckusick 	  if (acc < B)
17651748Smckusick 	    acc = 0;
17751748Smckusick 	  else
17851748Smckusick 	    acc = (acc >> 16) | -B;
17951748Smckusick 	}
18051748Smckusick 
18151748Smckusick       q[j] = qhat;
18251748Smckusick 
18351748Smckusick       /* Quotient may have been too high by 1.  If dividend went negative,
18451748Smckusick 	 decrement the quotient by 1 and add the divisor back.  */
18551748Smckusick 
18651748Smckusick       if ((signed long) (acc + u0[j]) < 0)
18751748Smckusick 	{
18851748Smckusick 	  q[j] -= 1;
18951748Smckusick 	  acc = 0;
19051748Smckusick 	  for (i = little_end (n); is_not_msd (i, n); i = next_msd (i))
19151748Smckusick 	    {
19251748Smckusick 	      acc += (unsigned long) (u + j)[i] + v[i];
19351748Smckusick 	      (u + j)[i] = acc & low16;
19451748Smckusick 	      acc = acc >> 16;
19551748Smckusick 	    }
19651748Smckusick 	}
19751748Smckusick     }
19851748Smckusick 
19951748Smckusick   /* Now the remainder is what's left of the dividend, shifted right
20051748Smckusick      by the amount of the normalizing left shift at the top.  */
20151748Smckusick 
20251748Smckusick   r[big_end (n)] = bshift (u + 1 + little_end (j - 1),
20351748Smckusick 			   16 - d,
20451748Smckusick 			   r + little_end (2),
20551748Smckusick 			   u[little_end (m - 1)] >> d,
20651748Smckusick 			   n - 1);
20751748Smckusick }
20851748Smckusick 
20951748Smckusick /* Left shift U by K giving W; fill the introduced low-order bits with
21051748Smckusick    CARRY_IN.  Length of U and W is N.  Return carry out.  K must be
21151748Smckusick    in 0 .. 16.  */
21251748Smckusick 
21351748Smckusick static int
21451748Smckusick bshift (u, k, w, carry_in, n)
21551748Smckusick      unsigned short *u, *w, carry_in;
21651748Smckusick      int k, n;
21751748Smckusick {
21851748Smckusick   unsigned long acc;
21951748Smckusick   int i;
22051748Smckusick 
22151748Smckusick   if (k == 0)
22251748Smckusick     {
22351750Smckusick       while (n-- > 0)
22451750Smckusick         *w++ = *u++;
22551748Smckusick       return 0;
22651748Smckusick     }
22751748Smckusick 
22851748Smckusick   acc = carry_in;
22951748Smckusick   for (i = little_end (n); is_not_msd (i, n); i = next_msd (i))
23051748Smckusick     {
23151748Smckusick       acc |= (unsigned long) u[i] << k;
23251748Smckusick       w[i] = acc & low16;
23351748Smckusick       acc = acc >> 16;
23451748Smckusick     }
23551748Smckusick   return acc;
23651748Smckusick }
237