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.

Do signed division words 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*/). Gforth implements these words as floored (since Gforth 0.7), but there are systems that implement them as symmetric. 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.

In the following, “remainder” (symmetric) has the same sign as the dividend or is 0, while “modulus” (floored) has the same sign as the divisor or is 0.

The following words perform 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”

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

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

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

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

u1=u2*u4+u3; u3 is the modulus, u4 the quotient

The following words perform 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 ARM A64 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.

The following words perform 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 Forth-2012 u3 is only allowed to be a positive signed number.

You can use the environmental query floored (see Environmental Queries) to learn whether / mod /mod */ */mod m*/ use floored or symmetric division on the system your program is being loaded on; alternatively, -1 3 / also produces -1 on floored and 0 on symmetric systems.

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.