(From a post on Compu-serve by Neil Rubenking.)

There was some talk a while back about the fact that the Comp data type is a second-class citizen - there are no routines to go from Comp to string and back. I'm working on a remedy, and I have a unit that's in pretty good shape. It includes CompToStr, CompToHex, StrToComp, and the helper functions CMod and CDiv, which implement MOD and DIV for Comp.

I found out something interesting in implementing the CMod and CDiv functions. It appears that the division operation on Comp variables *ROUNDS* the result rather than truncating it as one might expect.

I've found some ODD things happening at the very ends of the Comp range. E.g. the FIRST time I attempt to use CompToStr on a string with the value $7FFF FFFF FFFF FFFD (broken up for clarity), I get a floating point exception without a specific location in my program. But if I try, try again I get no exception. WEIRD! Anyway, take a look at this unit and see if you find any cool uses for it.

You WILL realize from looking at this that the format of a Comp is simply two double-words jammed together. In effect, the high Dword is a LongInt and the low DWord is an unsigned double-word. I really don't know why Delphi and Object Pascal treat Comp as floating-point??
 

 unit Compfunc;

 interface
 TYPE
   CompAsTwoLongs = RECORD
      LoL, HiL : LongInt;
   END;
 CONST Two32TL: CompAsTwoLongs = (LoL:0; HiL:1);
 VAR   Two32: Comp ABSOLUTE Two32TL;

{Some operations fail erratically for values at the extreme  ends of the range of Comp}
CONST MaxCompTL: CompAsTwoLongs = (LoL:$FFFFFFF0; HiL:$7FFFFFFF);
VAR   MaxComp: Comp ABSOLUTE MaxCompTL;

FUNCTION CMod(Divisor, Dividend: Comp): Comp;
FUNCTION CDiv(Divisor: Comp; Dividend: LongInt): Comp;
FUNCTION CompToStr(C: Comp): String;
FUNCTION CompToHex(C: Comp; Len: Integer): String;
FUNCTION StrToComp(const S : String): Comp;

implementation
USES SysUtils;

FUNCTION CMod(Divisor, Dividend: Comp): Comp;
VAR Temp : Comp;
BEGIN
  {Note: / operator for Comps apparently ROUNDS
   result rather than truncating}
  Temp := Divisor / Dividend;
  Temp := Temp * Dividend;
  Result := Divisor - Temp;
  IF Result < 0 THEN Result := Result + Dividend;
END;

FUNCTION CDiv(Divisor: Comp; Dividend: LongInt): Comp;
BEGIN
  Result := Divisor / Dividend;
  IF Result * Dividend > Divisor THEN
    Result := Result - 1;
END;

FUNCTION CompToStr(C: Comp): String;
VAR Posn : Integer;
BEGIN
  IF C > MaxComp THEN
    Raise ERangeError.Create('Comp too large for conversion to string');
  IF C < 0 THEN Result := '-'+CompToStr(-C)
  ELSE
    BEGIN
      Result := '';
      Posn := 0;
        WHILE TRUE DO
          BEGIN
            Result := Char(Round($30 + CMod(C,10)))+Result;
            IF C < 10 THEN Break;
            C := CDiv(C,10);
            Inc(Posn);
            IF Posn MOD 3 = 0 THEN Result := ','+Result;
          END;
    END;
END;

FUNCTION CompToHex(C: Comp; Len: Integer): String;
BEGIN
  IF (CompAsTwoLongs(C).HiL = 0) AND (Len <= 8) THEN
    Result := IntToHex(CompAsTwoLongs(C).LoL, Len)
  ELSE
    Result := IntToHex(CompAsTwoLongs(C).HiL, Len-8) +
      IntToHex(CompAsTwoLongs(C).LoL, 8)
END;

FUNCTION StrToComp(const S : String): Comp;
VAR Posn : Integer;
BEGIN
  IF S[1] = '-' THEN
    Result := -StrToComp(Copy(S,2,Length(S)-1))
  ELSE
    IF S[1] = '$' THEN {Hex string}
      try
        IF Length(S) > 9 THEN
          BEGIN
            {If string is invalid, exception raised by StrToInt}
            Result := StrToInt('$'+Copy(S,Length(S)-7, 8));
            IF Result < 0 THEN Result := Result + Two32;
            {If string is invalid, exception raised by StrToInt}
            CompAsTwoLongs(Result).HiL :=
              StrToInt(Copy(S,1,Length(S)-8))
          END
        ELSE
          BEGIN
            {If string is invalid, exception raised by StrToInt}
            Result := StrToInt(S);
            IF Result < 0 THEN Result := Result + Two32;
          END;
      except
        ON EConvertError DO Raise
          EConvertError.Create(S+' is not a valid Comp');
      end
    ELSE {Decimal string}
      BEGIN
        Posn := 1;
        Result := 0;
        WHILE Posn <= Length(S) DO
          CASE S[Posn] OF
            ',': Inc(Posn);
            '0'..'9': BEGIN
              Result := Result * 10 + Ord(S[Posn])-$30;
              Inc(Posn);
            END;
            ELSE Raise EConvertError.Create(S+
              ' is not a valid Comp');
          END;
      END;
END;

end.