On integer division

1 Integer division

The integer division of an integer  , the dividend, by another non nul integer  , the divisor, is the determination of two integers: the quotient  and the remainder  such that  and  . With this definition, there are two results if  is not a divisor of  . To make the result unique, one has to impose an additional condition.

Three such conditions are in use. But first a word about the notations used in this document. / is the real division of  by  . When an uniquifying condition is implied by the context, % is the quotient and rem is the remainder of an integer division. When the uniquifying condition is not important  is the quotient and mod is the remainder.

When defining the uniquifying conditions, we’ll also make use the two functions:

      8>!1       0               8>            0
      <                        < 
sgn      >:0        0       trunc     >:0        0
       1        0                            0

1.1 Truncating division

The truncating division can be defined in two equivalent ways: one relating the value of  with the result of the real division (which explains the name we use), the other binding the sign of  with the sign of the dividend:


   trunc  --
0  sgn     sgn
This way of uniquifying the result of the division brings us some properties:
    !   %!        %
!   %        %!      !(  %  )
  !   rem      !(  rem   )

     rem !        rem
That is a lot of symmetries around the value 0  ; for the quotient, they are the same one that the real division has. These symmetries implies that 0  is special (for instance, there are 2   !1  integers giving 0  when divided by  when there are only  such integers for other quotients).

1.2 Floor division

Again, two equivalent ways of defining the division:

  j   k
       --
0  sgn     sgn
The properties we get are:
   !   %!        %
!   rem !      !(  rem   )
(         )rem         rem
We loose some of the symmetries around the origin, but now the remainder is periodic and 0  is now behaving like other numbers.

1.3 Positive remainder division

There is only one condition to define this division:

05
The properties are:
     %!      !(  %   )

    rem !        rem
(         )rem         rem
Again a periodical remainder and less symmetries that for the truncating division.

To illustrate, here are some examples:

           truncating  floor positiveremainder
--------------------------------------------
   7 %3        2       2          2
   7rem3        1       1          1
   !7%3       !2      !3         !3
  !7rem3      !1       2          2
   7%!3       !2      !3         !2
  7rem !3       1      !2          1

  !7 %!3       2       2          3
-!7rem-!3-----!1------!1----------2---------

Mathematicians tend to define the version they want before using it if it is important. Programmers often don’t have that luxery and have to make with what the system they use provides them. It happens that processors and languages seem to have standardized on the floor division even if the other two (they differ only for negative dividend, which seems rare) appear to be more in use among the mathematicians. Exceptions exist, some languages even provides two remainders (the truncating and the floor one).

Going from one version to the other isn’t difficult, but it can be tedious. It is just adding or substracting one to the quotient, and adding or substracting the divisor to the remainder depending on the sign of the dividend and the divisor. As an example, here is an algorithm computing the floor division assuming that the truncating one is available (in practice the most common case):


Algorithm 1: Transforming truncating division in floor division
 A    %
 A    rem
 if 0   then O Doing 0W      0  may be preferable as it will not overflow.
  A   !1
  A
 end if

2 Long division

In this section, we’ll see how to divide a  digit number  whose digits in some base c are (       [[[      )
        !1   1 0  by a  digits number  whose digits are (    [[[      )
   !1   1 0  using only numbers less that c2   .

The algorithm we’ll use is the long division, the pencil and paper method by which we have learned how to do division. We’ll consider that there is an additional digit for  : 
 , currently its value is 0  and it just helps to take into account to fact that such division sometimes produces  digits and sometimes !1  ; it will be needed in the final version of the algorithm anyway. The algorithm is thus the following:


Algorithm 2: Generic long division
 for [[[0   do
  j       c
  (        [[[      1    )A (       [[[     1    )!    (    !1[[[  1 0)
 end for

At the end, (    [[[  1 0)  is the quotient and the remainder is (   !1[[[  1 0)  .

The only difficulty in applying this algorithm is getting the next digit:

     c

for which we known that 05       c .

Well, it is easy to do it if 1  :


Algorithm 3: Long division, short divisor
 for [[[0   do
  j     1c     k A  -- -0---
  (        [[[      1    )A (       [[[     1    )!      0
 end for

In the other cases, as we are using numbers less that  2
c   , the obvious idea is the use

        c       !1
ˆ    -- -------
        !1

(we are temporarily dropping the  indices). Let’s try to find out how near we are from the true value. First we deduce:


ˆ       c-------!1
            !1
         c         !1c  !1
     --- ----c -!1---
            !1
     ----- -!1
     j  k!1c
 6  --

 6

Then bound out the other direction:

           c          c  !1
ˆ !         --------!-1!1--- !
               !1c
         -------- !
            !1c   !1
         -------- !  - - 1
           !1c   !1
          as     j --k    -!1

        --------!  - - 1
          !1c    !1
       --   !----!1c--!1
                !1c   !1     1
         c  !1
       c ----c -!1   1
        c !1
       ----  1
          !1

so if !16 c/2  then 0 5ˆ  !  52  . Knuth [1, section 4.3.1] using


ˆ  max (     c  -------!1c---!1 \c !1)
              !1c   !1

shows that then !1 6  c/2 is sufficient.

In order to tighen our bound on ˆ , let’s try to impose                     2
ˆ (    !1c       !2)5        c            !1c          !2   :

ˆ (    !1c       !2)5        c 2          !1c          !2
ˆ (    !1c       !2)5 (       c           !1)c           !2
ˆ      c   ˆ      5 (ˆ         ˆ )c
    !1       !2      !1             !2
        ˆ     !25 ˆc           !2

This last condition can be computed without overflow if ˆ    c and ˆ  c (if it isn’t, the condition is false).

We’ll then have

                      !1         !2
ˆ !           c   -----!1c -!1-------!!2c2---- !
               !1c          !2c
         -----------------  !
           !1c   !1      !2c  !2
         -----------------  ! --  1
           !1c  !1      !2c  !2
        -----------------!  --  1
          !1c  !1      !2c  !2
             !    !1c  !1!     !2c   !2
        -- - - -!1c -!1 - - -!2c -!2--   1
              c  !2
       c----- -!1------- -!2   1
           !1c          !2c
       -----c-----  1
          !1c       !2
       2

If one consider that !1  can differ of (     1) only in the last digit, one see that to be more precise, one need to take all the digits into account.

Let’s modify our initial algorithm using the above properties. To take advantage of the fact that if !16 c/2  then 0 5  ˆ!   52  , we’ll multiply both dividend and divisor by c/(    !1  1) (the dividend will need the additional digit we have already introduced, the divisor won’t need another digit, but its first digit will be 6  c/2 ) and divide the remainder by the same value at the end. We’ll first use ˆ      (   c        !1)/    !1 then adjust ˆ until ˆ    c   ˆ   !25 ˆ c           !2   (the condition in the algorithm is a little more complicated in order to prevent overflow). Then we are sure that ˆ is at most one too big, so after having subtracted ˆ from (        [[[      1    )  we check if the result had a borrow and add back  in that case. The result is algorithm 4.


Algorithm 4: Long division, full version
1:  First normalize the operand to ensure !16   c/2 .
2:  A c  (    !1  1)
3:  if 1   then
4:   (   !1[[[  1  0)A   (    !1[[[  1  0)
5:   (        [[[  1  0)A   (        [[[  1  0)
6:  end if
7:  Now determine each digits.
8:  for [[[0   do
9:   Get our estimate
10:   ˆ A (        c          !1)      !1
11:   ˆ A (       c           !1)mod ˆ     !1
12:   Refine if needed
13:   while ˆ 6 c   (ˆ    c  ˆ    !2  c ˆ           !2)   do
14:   ˆA ˆ !1
15:   ˆ A   ˆ      !1
16:   end while
17:   Do the substraction
18:   0
19:   for 0[[[  !1   do
20:   A        !     ˆ
21:   A    mod c
22:   A     c
23:   end for
24:   If there is a borrow, that mean the refinement wasn’t enough to get it right, add back  and adjust ˆ .
25:   if W  0   then
26:   (       !1[[[     1    )A (       !1[[[     1    )  (    !1[[[  1  0)
27:   ˆA ˆ !1
28:   end if
29:   A ˆ
30:  end for
31:  Finally undo the adjustment of the remainder
32:  if 1   then
33:   (   !1[[[  1 0)A (    !1[[[  1  0) O Use algorithm 3
34:  end if

For c even c/2    c /2  , the refinement loop will be executed at most twice, so unrolling the loop should be considered in an implementation.

With c odd, it is possible to get ˆ       3  (for example 48789   8# 4889  4879   and ˆ  489/4   129   8   3  ). But as the additional condition we use to ensure ˆ 5      1  doesn’t depend on !1   , unrolling the loop twice would be enough, the add back step will handle final adjustment.

References

[1]   Donald E. Knuth. The Art of Computer Programming, Volume 2: Seminumerical Algorithms. Addison-Wesley Longman, third edition, 1998.

[2]   Henry S. Warren, Jr. Hacker’s Delight. Addison-Wesley, 2003.