6.5.4 Integer division

Below you find a considerable number of words for dealing with divisions. A major difference between them is in dealing with signed division: Do the words support signed division (those with the U prefix do not)?

If they do, do they round towards negative infinity (floored division, suffix F), or towards 0 (symmetric division, suffix S). The standard leaves the issue implementation-defined for most standard words (/ mod /mod */ */mod m*/), and different systems have made different choices. Gforth implements these words as floored (since Gforth 0.7). There is only a difference between floored and symmetric division if the dividend and the divisor have different signs, and the dividend is not a multiple of the divisor. The following table illustrates the results:

                      floored          symmetric
dividend divisor remainder quotient remainder quotient
    10      7           3   1              3   1
   -10      7           4  -2             -3  -1
    10     -7          -4  -2              3  -1
   -10     -7          -3   1             -3   1

The common case where floored vs. symmetric makes a difference is when dividends n1 with varying sign are divided by the same positive divisor n2; in that case you usually want floored division, because then the remainder is always positive and does not change sign depending on the dividend; also, with floored division, the quotient always increases by 1 when n1 increases by n2, while with symmetric division there is no increase in the quotient for -n2<n1<n2 (the quotient is 0 in this range).

In any case, if you divide numbers where floored vs. symmetric makes a difference, you should think about which variant is the right one for you, and then use either the appropriately suffixed Gforth words, or the standard words fm/mod or sm/rem.

Single-by-single-cell division:

/ ( n1 n2 – n  ) core “slash”

n=n1/n2

/s ( n1 n2 – n ) gforth-1.0 “slash-s”
/f ( n1 n2 – n ) gforth-1.0 “slash-f”
u/ ( u1 u2 – u ) gforth-1.0 “u-slash”
mod ( n1 n2 – n  ) core “mod”

n is the modulus of n1/n2

mods ( n1 n2 – n ) gforth-1.0 “mod-s”
modf ( n1 n2 – n ) gforth-1.0 “modf”
umod ( u1 u2 – u ) gforth-1.0 “umod”
/mod ( n1 n2 – n3 n4  ) core “slash-mod”

n1=n2*n4+n3; n3 is the modulus, n4 the quotient.

/mods ( n1 n2 – n3 n4 ) gforth-1.0 “slash-mod-s”

n3 is the remainder, n4 the quotient

/modf ( n1 n2 – n3 n4 ) gforth-1.0 “slash-mod-f”

n3 is the modulus, n4 the quotient

u/mod ( u1 u2 – u3 u4 ) gforth-1.0 “u-slash-mod”

u3 is the modulus, u4 the quotient

Double-by-single-cell division with single-cell results; these words are roughly as fast as the words above on some architectures (e.g., AMD64), but much slower on others (e.g., an order of magnitude on various Aarch64 CPUs).

fm/mod ( d1 n1 – n2 n3 ) core “f-m-slash-mod”

Floored division: d1 = n3*n1+n2, n1>n2>=0 or 0>=n2>n1.

sm/rem ( d1 n1 – n2 n3 ) core “s-m-slash-rem”

Symmetric division: d1 = n3*n1+n2, sign(n2)=sign(d1) or 0.

um/mod ( ud u1 – u2 u3 ) core “u-m-slash-mod”

ud=u3*u1+u2, 0<=u2<u1

du/mod ( d u – n u1 ) gforth-1.0 “du-slash-mod”

d=n*u+u1, 0<=u1<u; PolyForth style mixed division

*/ ( ( n1 n2 n3 – n4  ) core “star-slash”

n4=(n1*n2)/n3, with the intermediate result being double

*/s ( n1 n2 n3 – n4 ) gforth-1.0 “star-slash-s”

n4=(n1*n2)/n3, with the intermediate result being double

*/f ( n1 n2 n3 – n4 ) gforth-1.0 “star-slash-f”

n4=(n1*n2)/n3, with the intermediate result being double

u*/ ( u1 u2 u3 – u4 ) gforth-1.0 “u-star-slash”

u4=(u1*u2)/u3, with the intermediate result being double.

*/mod ( n1 n2 n3 – n4 n5  ) core “star-slash-mod”

n1*n2=n3*n5+n4, with the intermediate result (n1*n2) being double; n4 is the modulus, n5 the quotient.

*/mods ( n1 n2 n3 – n4 n5 ) gforth-1.0 “star-slash-mod-s”

n1*n2=n3*n5+n4, with the intermediate result (n1*n2) being double; n4 is the remainder, n5 the quotient

*/modf ( n1 n2 n3 – n4 n5 ) gforth-1.0 “star-slash-mod-f”

n1*n2=n3*n5+n4, with the intermediate result (n1*n2) being double; n4 is the modulus, n5 the quotient

u*/mod ( u1 u2 u3 – u4 u5 ) gforth-1.0 “u-star-slash-mod”

u1*u2=u3*u5+u4, with the intermediate result (u1*u2) being double.

Division with double-cell results; these words are much slower than the words above.

ud/mod ( ud1 u2 – urem udquot  ) gforth-0.2 “ud/mod”

divide unsigned double ud1 by u2, resulting in a unsigned double quotient udquot and a single remainder urem.

m*/ ( d1 n2 u3 – dquot  ) double “m-star-slash”

dquot=(d1*n2)/u3, with the intermediate result being triple-precision. In ANS Forth u3 can only be a positive signed number.

You can use the following environmental query floored (see Environmental Queries) to learn whether / mod /mod */ */mod m*/ use floored or symmetric division.

One other aspect of the integer division words is that most of them can overflow, and division by zero is mathematically undefined. What happens if you hit one of these conditions depends on the engine, the hardware, and the operating system: The engine gforth tries hard to throw the appropriate error -10 (Division by zero) or -11 (Result out of range), but on some platforms throws -55 (Floating-point unidentified fault). The engine gforth-fast may produce an inappropriate throw code (and error message), or may produce no error, just produce a bogus value. I.e., you should not bet on such conditions being thrown, but for quicker debugging gforth catches more and produces more accurate errors than gforth-fast.