November 10, 2026•7 min read

Pascal Units: One Average, Two Answers

The same two scores print 7 in one program and 7.50 in another, because each copy of the function divides differently. A unit gives it one body, and the interface decides who may call it.

Read in Indonesian

Two Pascal programs read the same two scores, 7 and 8, and compute the average. One prints mean: 7. The other prints mean: 7.50. Both compile without a complaint, both run without a crash, and neither one is lying about what you wrote. Each program carries its own copy of the average function, and the copies do not match. Fixing one copy leaves the other broken, and nothing warns you that they have drifted apart. A unit fixes this by giving the function exactly one body.

Key takeaways

  • div drops the remainder, so (7 + 8) div 2 is 7 while (7 + 8) / 2 is 7.5.
  • A unit has two halves: interface is what other programs may call, implementation is the body only the unit itself sees.
  • A function that lives only in implementation compiles inside the unit and fails at the caller with Error: Identifier not found.
  • Free Pascal compiles a unit it finds next to the program and leaves a .ppu behind; from a subfolder the flag is -Fulib, attached, because -Fu lib with a space does not find it.
  • The unit name is looked up as a file name, so unit stats; must live in stats.pas.

The remainder that disappeared

Start with the arithmetic, because the bug lives there. Pascal has two division operators, and they answer different questions.

PASCAL
program divide;

var
  a, b: integer;

begin
  a := 7;
  b := 8;
  writeln('(a + b) div 2 = ', (a + b) div 2);
  writeln('(a + b) / 2   = ', (a + b) / 2:0:1);
  writeln('unformatted   = ', (a + b) / 2);
end.
Output
$ fpc divide.pas && ./divide
(a + b) div 2 = 7
(a + b) / 2   = 7.5
unformatted   =  7.5000000000000000E+000

div is integer division, so it throws the remainder away and (7 + 8) div 2 is 7. / always produces a real number, so the same sum gives 7.5. The last line shows how Pascal prints a raw real: scientific notation, with a leading space. Remember that for later.

Here is the program that printed the short answer. Mean takes integers and uses div, so the 7.5 never exists.

PASCAL
program average;

var
  a, b: integer;

function Mean(x, y: integer): integer;
begin
  Mean := (x + y) div 2;
end;

begin
  a := 7;
  b := 8;
  writeln('scores: ', a, ' and ', b);
  writeln('mean: ', Mean(a, b));
end.
Output
$ fpc average.pas && ./average
scores: 7 and 8
mean: 7

This is the first copy. Any program that pastes in this function inherits the truncation, and any program that pastes in a / version gets the other answer.

A unit has two halves

A unit is a file split by two keywords. Everything under interface is a contract: the names, parameters and return types that other programs may call. Everything under implementation is the body, the code that does the work, visible only inside the unit. Here is stats.pas, which takes over the function.

PASCAL
unit stats;

interface

function Mean(x, y: real): real;
function Format2(n: real): string;

implementation

function Mean(x, y: real): real;
begin
  Mean := (x + y) / 2;
end;

function Format2(n: real): string;
begin
  Str(n:0:2, Format2);
end;

end.

Mean now takes and returns real and uses /. Each signature appears twice, once per half: the interface line promises, the implementation line delivers. Format2 wraps Str(n:0:2, ...), which writes a real with two decimals into a string, so callers get 7.50 instead of scientific notation. A program opts in with one line, uses stats;, placed right after the program line.

The compiler builds it for you

PASCAL
program average;

uses stats;

var
  a, b: integer;

begin
  a := 7;
  b := 8;
  writeln('scores: ', a, ' and ', b);
  writeln('mean: ', Format2(Mean(a, b)));
end.
Output
$ fpc average.pas && ./average
scores: 7 and 8
mean: 7.50

There was no separate command for the unit. When the compiler reaches uses stats;, it looks for stats.pas next to the program and compiles it on the way. Afterward, stats.o and stats.ppu sit beside the program. The .ppu file is the compiled form of the unit that Free Pascal leaves behind.

Without a flag, the compiler finds the unit only when it sits next to the program. Move stats.pas into a folder named lib, and the build stops:

Output
$ fpc main.pas
main.pas(3,6) Fatal: Can't find unit stats used by main
Fatal: Compilation aborted

Adding the flag with a space changes nothing: fpc -Fu lib main.pas prints the same two lines. The flag must be attached to its path, so fpc -Fulib main.pas compiles, and the program prints scores: 7 and 8 and mean: 7.50.

The name matters too. The compiler turns uses stats; into a file name. Save unit stats; as stat2.pas, and a program that uses it cannot find it:

Output
$ fpc prog2.pas
prog2.pas(3,6) Fatal: Can't find unit stats used by prog2
Fatal: Compilation aborted

So unit stats; must live in stats.pas. The fix is the file name, not the code.

What the interface gates

The two halves are not decoration. Suppose a unit hidden.pas declares nothing in interface and implements Mean under implementation. The unit compiles. A program uses_hidden.pas that says uses hidden; and calls Mean does not:

Output
$ fpc uses_hidden.pas
uses_hidden.pas(6,21) Error: Identifier not found "Mean"
uses_hidden.pas(8) Fatal: There were 1 errors compiling module, stopping
Fatal: Compilation aborted

Mean exists in the file, and the caller still cannot see it, because implementation is private to the unit. To share a function, put its header in interface. To keep a helper to yourself, leave it out.

Two files, one command

Eyeballing output gets old, so let a second program do the comparing. check_average.pas reads lines from standard input and compares the first two against the expected text.

PASCAL
program check_average;

var
  expected: array[1..2] of string;
  line: string;
  i, passed, failed: integer;

begin
  expected[1] := 'scores: 7 and 8';
  expected[2] := 'mean: 7.50';

  passed := 0;
  failed := 0;
  i := 0;

  while not eof do
  begin
    readln(line);
    i := i + 1;
    if i <= 2 then
    begin
      if line = expected[i] then
      begin
        writeln('PASS  line ', i, ': ', expected[i]);
        passed := passed + 1;
      end
      else
      begin
        writeln('FAIL  line ', i, ': expected "', expected[i], '", got "', line, '"');
        failed := failed + 1;
      end;
    end;
  end;

  if i < 2 then
  begin
    writeln('FAIL  the program printed ', i, ' lines, expected 2');
    failed := failed + 1;
  end;

  writeln;
  writeln(passed, ' of 2 checks passed');

  if failed > 0 then
    ExitCode := 1;
end.

Build it once with fpc check_average.pas. After that the whole check is one command, ./average | ./check_average, where the pipe feeds what average prints into the checker. Build the starter average.pas from the first section and run it:

Output
$ ./average | ./check_average
PASS  line 1: scores: 7 and 8
FAIL  line 2: expected "mean: 7.50", got "mean: 7"

1 of 2 checks passed

The checker names the line and shows both strings. Now switch average.pas to the version that uses stats, rebuild, and run the same command:

Output
$ ./average | ./check_average
PASS  line 1: scores: 7 and 8
PASS  line 2: mean: 7.50

2 of 2 checks passed

Same checker, same command, new answer.

Exercise. In the unit version of average.pas, change the last writeln to writeln('mean: ', Mean(a, b)); and drop Format2. Predict what the line prints, then run the checker. The line becomes mean: 7.5000000000000000E+000, with two spaces after the colon, and it fails line 2. That is why Format2 exists.

When two programs need the same calculation, write it once in a unit and let both of them uses it, because a pasted copy is a bug waiting for the first edit.

Get better at shipping code.

Join the other developers getting tech updates, tips, and relaxed tutorials in their inbox every week.

Stay in the loop

Tech updates, coding tips, and relaxed insights straight to your inbox every week.

No spam. Unsubscribe anytime.

Read next

See all
Struktur DataWhat a Linked List Actually Is

A linked list is a linear chain of nodes, each holding data and a pointer to the next node. Here is what that chain looks like, what it costs next to an array, and the same minimal implementation in Java, Python, and JavaScript.

For developers who learn by struggling

© 2026 Inva.dev.