[TUTOR] Division, Rounding, and Modulus

Chris H.'s Ultima / ACS-style game development system!

Moderators: Ice Cream Jonsey, joltcountry

Admiral Ackguh
Posts: 137
Joined: Sat Nov 03, 2012 11:26 am
Location: Canada
Contact:

[TUTOR] Division, Rounding, and Modulus

Post by Admiral Ackguh »


The division operation in ACK rounds up if the fraction is 0.5 or more. In games, and many other applications, rounded integer division is useful. So is truncated integer division, where there is no rounding, and the fraction simply discarded. Here are the full, truncated, and rounded results of dividing by five:

Code: Select all

x      0   1   2   3   4   5   6   7   8   9  10  11  12  13
x/5    0  0.2 0.4 0.6 0.8  1  1.2 1.4 1.6 1.8  2  2.2 2.4 2.6
trunc  0   0   0   0   0   1   1   1   1   1   2   2   2   2 
round  0   0   0   1   1   1   1   1   2   2   2   2   2   3
Some more rounded division results:

Code: Select all

x      0   1   2   3   4   5   6   7   8   9  10  11  12  13  14
x/2    0   1*  1   2   2   3   3   4   4   5   5   6   6   7   7
x/3    0   0   1*  1   1   2   2   2   3   3   3   4   4   4   5
x/4    0   0   1*  1   1   1   2   2   2   2   3   3   3   3   4
x/5    0   0   0   1*  1   1   1   1   2   2   2   2   2   3   3
x/6    0   0   0   1*  1   1   1   1   1   2   2   2   2   2   2
x/10   0   0   0   0   0   1*  1   1   1   1   1   1   1   1   1
* indicates the rounding point, the first x value triggering rounding.

R (if y even) = y / 2
R (if y odd) = (y+1) / 2

It is possible to have truncated integer division with the following formula, using R the rounding point:

x div y = ((x + R) / y) - 1

The code must add, divide, and subtract in that order. R is used as an offset, to force the built-in rounding of the ACK division operator to round at multiples of y. Since R is added, the division result is one greater than what we need, hence subtraction by 1. A simpler (but useless in ACK) formula would be ((x - R) / y). For x < R, the negative x - R would overflow the variable used to store the intermediate result, and division would treat it as a huge positive value.

Code: Select all

x         0   1   2   3   4   5   6   7   8   9  10  11  12  13
x/3       0   0   1   1   1   2   2   2   3   3   3   4   4   4 
&#40;x+2&#41;/3   1   1   1   2   2   2   3   3   3   4   4   4   5   5 
above-1   0   0   0   1   1   1   2   2   2   3   3   3   4   4 

Code: Select all

x         0   1   2   3   4   5   6   7   8   9  10  11  12  13
x/6       0   0   0   1   1   1   1   1   1   2   2   2   2   2
&#40;x+3&#41;/6   1   1   1   1   1   1   2   2   2   2   2   2   3   3
above-1   0   0   0   0   0   0   1   1   1   1   1   1   2   2
Here is some sample macro code, given X and Y, using R as temporary, and Q for the result (quotient):

Code: Select all

        08&#58; SET R = Y
        09&#58; IF R & 1 THEN
        10&#58; SET R = R + 1
        11&#58; SET R = R / 2
        12&#58; SET Q = X + R
        13&#58; SET Q = Q / Y
        14&#58; SET Q = Q - 1
If Y is a constant, values for it and R can be substituted:

Code: Select all

        12&#58; SET Q = X + 3
        13&#58; SET Q = Q / 6
        14&#58; SET Q = Q - 1
The formula can be used with non-standard values of R, as shown below, for division by 6, to round at various points. Note that when R equals y, the result is the same as normal rounded division.

Code: Select all

x         0   1   2   3   4   5   6   7   8   9  10  11  12  13
R = 3     0   0   0   0   0   0   1   1   1   1   1   1   2   2
R = 4     0   0   0   0   0   1   1   1   1   1   1   2   2   2
R = 5     0   0   0   0   1   1   1   1   1   1   2   2   2   2
R = 6     0   0   0   1   1   1   1   1   1   2   2   2   2   2
R = 7     0   0   1   1   1   1   1   1   2   2   2   2   2   2
R = 8     0   1   1   1   1   1   1   2   2   2   2   2   2   3

MODULUS: For positive integers, the modulus is the same as the division remainder. It is basically a way to force x between 0 and y-1.

x mod y = x - (y * (x div y))
x div y = ((x + R) / y) - 1
x mod y = x - (y * (((x + R) / y) - 1))

As with truncated division, the code must perform each operation in order, starting from the innermost brackets. Add, divide, subtract, multiply, and subtract. Do not algebraically simplify the formula, as multiplication does not cancel out either truncated or rounded division!

Code: Select all

x         0   1   2   3   4   5   6   7   8   9  10  11  12  13
x div 3   0   0   0   1   1   1   2   2   2   3   3   3   4   4
3*Q       0   0   0   3   3   3   6   6   6   9   9   9  12  12
x-&#40;3*Q&#41;   0   1   2   0   1   2   0   1   2   0   1   2   0   1

x         0   1   2   3   4   5   6   7   8   9  10  11  12  13
x/6       0   0   0   1   1   1   1   1   1   2   2   2   2   2
&#40;x+3&#41;/6   1   1   1   1   1   1   2   2   2   2   2   2   3   3
x div 6   0   0   0   0   0   0   1   1   1   1   1   1   2   2
6*Q       0   0   0   0   0   0   6   6   6   6   6   6  12  12
x-&#40;6*Q&#41;   0   1   2   3   4   5   0   1   2   3   4   5   0   1
General macro code for modulus, where M is result:

Code: Select all

        08&#58; SET R = Y
        09&#58; IF R & 1 THEN
        10&#58; SET R = R + 1
        11&#58; SET R = R / 2
        12&#58; SET M = X + R
        13&#58; SET M = M / Y
        14&#58; SET M = M - 1
        15&#58; SET M = M * Y
        16&#58; SET M = X - M
As above for Y of 6:

Code: Select all

        12&#58; SET M = X + 3
        13&#58; SET M = M / 6
        14&#58; SET M = M - 1
        15&#58; SET M = M * 6
        16&#58; SET M = X - M
For both truncated quotient and modulus:

Code: Select all

        12&#58; SET Q = X + 3
        13&#58; SET Q = Q / 6
        14&#58; SET Q = Q - 1
        15&#58; SET M = Q * 6
        16&#58; SET M = X - M
- A:A: