pythonfreepascal

Mod operator in Free Pascal gives a different result than expected


The mod operator in Free Pascal does not produce the results I would expect.

This can be demonstrated by the program below whose output does not agree with the result of the same calculation in Python (or Google).

program test(output);

var
    a, b, c: longint;

begin
    a := -1282397916;
    b := 2147483647;
    c := a mod b;
    writeln (c:16);
end.
     -1282397916

Compare this to the output of the Python script below which gives the result I expected.

#!/usr/bin/python

a = -1282397916
b = 2147483647
c = a % b
print (c)
865085731

This is the same as the result as that obtained by pasting the following text into Google (and when using the mod operator in VAX‑Pascal).

(-1282397916 % 2147483647)

The question is: Why does Free Pascal behave differently? And how do I get the same result as that obtained when using the mod operator in Python?


Solution

  • With respect to a non‑negative integral dividend and positive integral divisor there is no ambiguity. All programming languages do the same. Once you use other values though, programming languages differ.

    Pascal uses a Euclidean‑like definition of the modulus operator. In ISO standard 7185 (“Standard Pascal”), page 48, it is defined as follows:

    A term of the form i mod j shall be an error if j is zero or negative; otherwise, the value of i mod j shall be that value of (i − (k × j)) for integral k such that 0 ≤ i mod j < j.

    In other words:

    Evaluation of a term of the form x mod y is an error if y is less than or equal to zero; otherwise there is an integer k such that x mod y satisfies the following relation:

            0 <= x mod y = x − k * y < y.
    

    Source: Jensen, Kathleen; Wirth, Niklaus. Pascal – user manual and report (4th revised ed.). p. 168. doi:10.1007/978‑1‑4612‑4450‑9. ISBN 978‑0‑387‑97649‑5.

    Thus the result of the mod operator is guaranteed to be non-negative. Unfortunately, as you have already observed, the FreePascal Compiler does not adhere to the ISO standards. The FPC will only return the proper result if {$modeSwitch isoMod+} is set:

    program moduloConfusion(output);
        {$modeSwitch isoMod+}
        type
            integer = ALUSInt;
        var
            dividend, divisor: integer;
        begin
            dividend := -1282397916;
            divisor  :=  2147483647;
            writeLn(dividend mod divisor:16)
        end.
    

    Note, this affects the definition of the mod operator per compilation unit, so the RTL and everything else – unless recompiled – continues using the other definition internally.

    Rest assured, however, Delphi and the GPC (GNU Pascal Compiler) do work correctly without making jump through loops.


    If you want to get the same result as in Python, you need to define and use your own function (here in Extended Pascal):

    function modulo(protected dividend, divisor: integer): integer;
        begin
            modulo := (abs(dividend) mod abs(divisor)) * -1 pow ord(divisor < 0)
        end;
    

    There is no magic switch to make FreePascal’s mod behave exactly like Python’s %.