How can I write comparison functions for unsigned 64 bit integers for versions of the compiler without support for UInt64?

1.5k views Asked by At

How can I do that with Delphi 6? UInt64 is not known in Delphi 6. It was introduced in later versions.

var
  i, j: Int64;

  if UInt64(i) < UInt64(j) then ...


I am thinking of an asm procedure. 

function UInt64CompareLT(i, j: Int64): Boolean;
asm
 ???
end;

function UInt64CompareGT(i, j: Int64): Boolean;
asm
 ???
end;
3

There are 3 answers

1
David Heffernan On BEST ANSWER

The Int64Rec type from SysUtils is designed for the task of picking out the parts of a 64 bit integer.

If you happen to be using a Delphi that pre-dates this type, define it yourself:

type
  Int64Rec = packed record
    case Integer of
      0: (Lo, Hi: Cardinal); 
      1: (Cardinals: array [0..1] of Cardinal); 
      2: (Words: array [0..3] of Word);
      3: (Bytes: array [0..7] of Byte);
  end;

What's more, you only need a single function that returns -1 for less than, 1 for greater than and 0 for equals. Something like this:

function CompareUInt64(const i, j: Int64): Integer;
begin
  if Int64Rec(i).Hi < Int64Rec(j).Hi then
    Result := -1
  else if Int64Rec(i).Hi > Int64Rec(j).Hi then
    Result := 1
  else if Int64Rec(i).Lo < Int64Rec(j).Lo then
    Result := -1
  else if Int64Rec(i).Lo > Int64Rec(j).Lo then
    Result := 1
  else
    Result := 0;
end;

The idea is that you first compare the high order part, and only if that is equal do you then go on to compare the low order part.

This can be made simpler with a compare function for Cardinal.

function CompareCardinal(const i, j: Cardinal): Integer;
begin
  if i < j then
    Result := -1
  else if i > j then
    Result := 1
  else
    Result := 0;
end;

function CompareUInt64(const i, j: Int64): Integer;
begin
  Result := CompareCardinal(Int64Rec(i).Hi, Int64Rec(j).Hi);
  if Result = 0 then
    Result := CompareCardinal(Int64Rec(i).Lo, Int64Rec(j).Lo);
end;

Finally, should you need the boolean functions of your question, they can be implemented on top of this more general function.

0
Dmitry Bychenko On

There's no need in using assembler (but, sure, you can do it): you can compare hi and low parts of Int64 instead:

  function UInt64CompareLT(i, j: Int64): Boolean;
  begin
    if (LongWord(i shr 32) < LongWord(j shr 32)) then
      Result := true
    else if (LongWord(i shr 32) > LongWord(j shr 32)) then
      Result := false
    else if (LongWord(i and $FFFFFFFF) < LongWord(j and $FFFFFFFF)) then
      Result := true
    else
      Result := false;
  end;

  function UInt64CompareGT(i, j: Int64): Boolean;
  begin
    if (LongWord(i shr 32) > LongWord(j shr 32)) then
      Result := true
    else if (LongWord(i shr 32) < LongWord(j shr 32)) then
      Result := false
    else if (LongWord(i and $FFFFFFFF) > LongWord(j and $FFFFFFFF)) then
      Result := true
    else
      Result := false;
  end;
4
Disillusioned On

Using assembler is possible, but would bind your code to a specific machine architecture.
You can achieve this in pure Pascal as follows:

type
  //Delcare a variant record to have 2 ways to access the same data in memory.
  T64Bit = record
  case Integer of
    0: (I64: Int64;);
    1: (Small: Cardinal; Big: Cardinal);
  end;

var
  I, J: T64Bit;

begin
  //You can set the value via normal Int64 assignment as follows:
  I.I64 := 1;
  J.I64 := 2;

  //Then you can compare the "big" and "small" parts on the number using
  //unsigned 32-bit comparisons as follows.
  if (I.Big < J.Big) or ((I.Big = J.Big) and (I.Small< J.Small)) then

  //The logic is as follows... 
  //  If the big part is less than, the the small part doesn't matter
  //  If the big parts are equal, then the comparison of the small parts determines the result.