/*- * Copyright (c) 1992 The Regents of the University of California. * All rights reserved. * * %sccs.include.redist.c% */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)qdivrem.c 5.5 (Berkeley) 05/12/92"; #endif /* LIBC_SCCS and not lint */ /* Copyright (C) 1989, 1992 Free Software Foundation, Inc. This file is part of GNU CC. GNU CC 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. GNU CC 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 GNU CC; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* As a special exception, if you link this library with files compiled with GCC to produce an executable, this does not cause the resulting executable to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU General Public License. */ #include #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; unsigned long m, n; { unsigned long qhat, rhat; unsigned long acc; unsigned short array[12]; unsigned short *u, *v, *u0, *u1, *u2; unsigned short *v0; int d, qn; int i, j; if (m > 16 || n > 8) { #ifdef KERNEL panic("bdiv: out of space"); #else #define EMSG "bdiv: out of space." (void)write(STDERR_FILENO, EMSG, sizeof(EMSG) - 1); abort(); #endif } u = &array[0]; v = &array[m / sizeof(unsigned short)]; 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) { *r /= n; /* force a divide-by-zero trap */ return; } } /* 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) { while (n-- > 0) *w++ = *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; }