fast integer division

Tim Peters uunet!ksr!tim
Fri Jun 22 12:38:18 PDT 1990


>  [dgh]
>  ... in converting between base 10**4 and base 2**16, the long
>  unsigned dividend can be almost as large as (2**16)*(10**4), and so
>  none of the methods ... suggested are applicable.

So switch to base 2**3 <grin>.

>  Conditionals are bad news, so putting in a switch statement in my
>  code or a binary search in Tim's may be an ephemeral advantage.  You
>  can make multiplies faster with a bigger hardware array but
>  conditional branches are just getting (relatively) slower.

Ya, that's the problem in writing "efficient" portable code -- so much
depends on the architecture!  The KSR machine is one of the few I know
of where your multiply-and-shift-plus-switch stab runs faster than my
three-tests stab (although it would run faster still if it computed the
remainder by multiplying and subtracting).  Strangely enough, the
fastest way to solve the full problem on a Cray would be to use
floating-point arithmetic (while it's not obvious, for dividends less
than (2**16)*(10**4) there's a fast test-free branch-free way to fiddle
things so that Cray's unreliable floating divide nevertheless delivers a
perfect integer quotient on the first try).  Etc.

>  Earl Killian pointed out that there's an interesting discussion of
>  this stuff in section 4.4 in volume 2 of Knuth, especially exercise
>  9.

I can't find a real citation just now, but especially look at "Integer
Multiplication & Division on the HP Precision Architecture", an article
that appeared "somewhere" in the last couple years.  This develops some
interesting methods for integer division on a machine that can't
directly divide or multiply worth beans (no offense to any HP people
reading this <grin>).  Given a fixed integer divisor y, and a bound on
the possible dividends, they precompute integer constants (depending on
y and the dividend bound) r, b and m such that

floor(x/y) is (r*x+b) >> m

for all x within the bounds (warning:  their notation probably differs).

E.g., the method you sent out is a special case of this with y=10000,
bound on dividends around 2^16, r=839, b=0, and m=19 (and that 839 =
ceiling(2^19/(10000/16)) is a good starting clue as to why it works).

Alas, finding appropriate r, b, and m can be tricky, and m sometimes
turns out to be so large as to require faking triple-precision
operations.  A related method KSR uses avoids most of this complexity at
the cost of one test; however, we have the biggest multiplier array in
the industry <grin>, so it may be less suitable for other machines:

Problem:  Given fixed unsigned integers y and n, 2 <= y < 2^n, find a
cheap way to compute floor(x/y) for all integer x s.t. 0 <= x < 2^n.

A solution:

1) Precompute the integer r = floor(2^n/y).

2) The algorithm is then

   qhat := floor(x*r/2^n)
   ASSERT qhat IN {floor(x/y), floor(x/y)-1}

   IF x-qhat*y >= y THEN
      qhat := qhat + 1
   ENDIF
   ASSERT floor(x/y) = qhat

Possible subtleties:

1) Note that in the precomputation of r, the dividend (2^n) is an n+1
   bit number; n-bit division doesn't suffice (at least not directly)
   for this step.

2) floor(x*r/2^n) is simply the upper n bits of the 2n-bit product x*r;
   depending on n and your machine, you may be able to get that with one
   instruction or may need to fake it as a multiple-precision operation.

3) The ordering of the operations in "x-qhat*y >= y" is guaranteed to
   allow all the arithmetic to be done using unsigned n-bit arithmetic;
   if you rearrange it, and intend to use n-bit arithmetic, make sure
   you don't introduce an opportunity for n-bit overflow (e.g., do *not*
   rewrite it as "IF x >= y*(qhat+1)").

Notes:

1) For some special values of y it's possible to do better; e.g., if y
   is a power of 2 it's obviously possible to do better, but there are
   cases beyond that ...

2) If you want the remainder too, replace the IF block by

   rem := x - qhat*y
   IF rem >= y THEN
      qhat := qhat + 1
      rem  := rem - y
   ENDIF
   ASSERT floor(x/y) = qhat
   ASSERT 0 <= rem < y
   ASSERT x = qhat*y + rem

Probably little here that can't be deduced easily as an extension of
Knuth's proofs (see David's reference above), so I'll skip the proofs.

don't-have-time-to-type-'em-in-anyway<smile>-ly y'rs  - tim

Tim Peters   Kendall Square Research Corp
timaksr.com,  ksr!timaharvard.harvard.edu



More information about the Numeric-interest mailing list