I wrote a random unit that uses the same algorithm as BP does. It produces exact the same random numbers as the Random function of BP 7.0 (I heard, older BP versions used a different algorithm, though).
Just a couple of notes/questions:
- Is the DOS interrupt in Randomize call OK for DJGPP and EMX? I'm not really sure if it's good, but it seems to work. Any improvements welcome!
- The code is not meant as a "default" random procedure for gpc, but only for BP compatibility.
- Of course, RandReal should have the same name as Random, when function overloading exists.
- When range checking exists, NextRand must be compiled without it. (That's why I put "{$W-,R-,W+}" in :-)
- The type declarations should be replaced by machine-independent ones (or be predefined by the compiler in the future).
- Random produces exact the same random number sequence as BP's integer random. RandReal gives the same values an BP's Random for FPU types (e.g. Double). BP's Random for BP's own Real type differs from that by 0.5 (doesn't really matter, just if someone wants to compare the random values).
While writing it, I discovered some type casting bugs in gpc concerning signed/unsigned integers:
program x; var a:integer value -$80000000; begin Writeln(ShortWord((LongWord(a)*4) DIV $100000000)); {65534, should be 2} Writeln(Word((LongWord(a)*4) DIV $100000000)); {-2, should be 2} end.
Another thing is the implicit real->integer(!) conversion, e.g. "Writeln(Integer(2.2))" works (and gives 2).
Is this required by the standard? Otherwise I don't think it should work. At least, it shouldn't with "--borland-pascal".
Apropos of BP compatibility: I think many BP users wouldn't like to have to put something like "{$ifdef __gpc__}uses system;{$endif}" in their programs. Is there a way to do this easier? If not, could one be created (perhaps a compiler switch that uses a given unit in all compiled files automatically; though I don't think it should be set by default even with "--borland-pascal", since System is a "contrib". But people could then set an alias to "gpc --automake --executable-file-name --borland-pascal --uses="system" or so...)?
---
UNIT Rand;
INTERFACE
TYPE Cardinal=Word; Card16=ShortWord; Card32=Word; Card64=LongWord; Int32=Integer;
VAR RandSeed:Int32 VALUE 0;
FUNCTION Random(Range:Card16):Card16; FUNCTION RandReal:Real; PROCEDURE Randomize;
IMPLEMENTATION
{$W-,R-,W+} PROCEDURE NextRand; BEGIN RandSeed:=$8088405*RandSeed+1 END;
FUNCTION Random(Range:Card16):Card16; BEGIN NextRand; Random:=(Card32(RandSeed)*Card64(Range)) DIV $100000000 END;
FUNCTION RandReal:Real; BEGIN NextRand; RandReal:=RandSeed/4294967296.0+0.5 END;
{$IFDEF __DJGPP__}{$DEFINE DOS}{$ENDIF} {$IFDEF __EMX__}{$DEFINE DOS}{$ENDIF}
{$IFDEF DOS} PROCEDURE Randomize; VAR RandSeedLoHi:PACKED RECORD Lo,Hi:Card16 END ABSOLUTE RandSeed; BEGIN ASM("movb $44,%%ah int $0x21" :"=c"(RandSeedLoHi.Lo), "=d"(RandSeedLoHi.Hi) ::"eax","ebx","ecx","edx","esi","edi") END;
{$ELSE} FUNCTION Rand:Integer; C; PROCEDURE SRand(Seed:Cardinal); C; FUNCTION GetPID:Integer; C;
PROCEDURE Randomize; BEGIN SRand(GetPID); RandSeed:=Rand END; {$ENDIF} END.