Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Theres a UIntToStr in delphi to let you display UINT64 values, but where is StrToUInt to allow user to input 64 bit unsigned values?

I want to convert a large 64 bit value from decimal or hex string to 64 bit UINT64 data type. There is a UIntToStr to help converting the UINT64 to string, but no way to convert a 64 bit integer to a unsigned value, as a string. That means integer values greater than 2**63 can not be represented in decimal or hex, using the RTL. This is normally not a big deal, but it can happen that a user needs to input a value, as an unsigned integer, which must be stored into the registry as a 64 bit unsigned integer value.

procedure HandleLargeHexValue;

var
 x:UINT64;
begin

  x := $FFFFFFFFFFFFFFFE;

  try
  x := StrToInt('$FFFFFFFFFFFFFFFF'); // range error.
  except
    x := $FFFFFFFFFFFFFFFD;
  end;

  Caption := UintToStr(x);

end;

Update Val now works fine in Delphi XE4 and up. In XE3 and below Val('$FFFFFFFFFFFFFFFF') works but not Val('9223372036854775899'). As Roeland points out below in Quality Central 108740: System.Val had problems with big UInt64 values in decimal until Delphi XE4.

like image 321
Warren P Avatar asked May 20 '11 20:05

Warren P


2 Answers

UPDATE: In XE4 and later the RTL bug was fixed. This hack is only useful in Delphi XE3 or older

Well, if it ain't there, I guess I could always write it.

(I wrote a pretty good unit test for this too, but its too big to post here)

unit UIntUtils;

{ A missing RTL function written by Warren Postma. }

interface
  function TryStrToUINT64(StrValue:String; var uValue:UInt64 ):Boolean;
  function StrToUINT64(Value:String):UInt64;

implementation

uses SysUtils,Character;

{$R-}

function TryStrToUINT64(StrValue:String; var uValue:UInt64 ):Boolean;
var
  Start,Base,Digit:Integer;
  n:Integer;
  Nextvalue:UInt64;
begin
  result := false;
  Base := 10;
  Start := 1;
  StrValue := Trim(UpperCase(StrValue));
  if StrValue='' then
    exit;
  if StrValue[1]='-' then
    exit;
  if StrValue[1]='$' then
  begin
    Base := 16;
    Start := 2;
    if Length(StrValue)>17 then // $+16 hex digits = max hex length.
        exit;
  end;
  uValue := 0;
  for n := Start to Length(StrValue) do
  begin
      if Character.IsDigit(StrValue[n]) then
          Digit := Ord(StrValue[n])-Ord('0')
      else if  (Base=16) and (StrValue[n] >= 'A') and (StrValue[n] <= 'F') then
          Digit := (Ord(StrValue[n])-Ord('A'))+10
      else
          exit;// invalid digit.

      Nextvalue := (uValue*base)+digit;
      if (Nextvalue<uValue) then
          exit;
      uValue := Nextvalue;
  end;
  result := true; // success.
end;

function StrToUINT64(Value:String):UInt64;
begin
  if not TryStrToUINT64(Value,result) then
    raise EConvertError.Create('Invalid uint64 value');

end;

end.
like image 86
Warren P Avatar answered Nov 15 '22 20:11

Warren P


I must disagree that Val solves this issue. Val works only for big UInt64 values when they are written in Hex. When they are written in decimal, the last character is removed from the string and the resulting value is wrong.

See Quality Central 108740: System.Val has problems with big UInt64 values EDIT: It seems that this issue should be solved in XE4. Can't test this.

like image 22
Roeland Van Heddegem Avatar answered Nov 15 '22 20:11

Roeland Van Heddegem