4

just curious why the following code fails to convert uint64 value in string representation ?

var
  num: UInt64;
  s: string;
  err: Integer;

begin
  s := '18446744073709551615';  // High(UInt64)
  Val(s, num, err);
  if err <> 0 then
    raise Exception.Create('Failed to convert UInt64 at ' + IntToStr(err));  // returns 20
end.

Delphi XE2

Am I missing something here ?

David Unric
  • 7,421
  • 1
  • 37
  • 65
  • So far as I can tell, there is no built in routine that converts a string to a UInt64. You'll have to write your own! – David Heffernan Aug 04 '12 at 13:20
  • 2
    This question (http://stackoverflow.com/questions/6077258/theres-a-uinttostr-in-delphi-to-let-you-display-uint64-values-but-where-is-str) covers the same topic. – David Heffernan Aug 04 '12 at 13:21

4 Answers4

5

You are right: Val() is not compatible with UInt64 / QWord.

There are two overloaded functions:

  • One returning a floating point value;
  • One returning an Int64 (i.e. signed value).

You can use this code instead:

function StrToUInt64(const S: String): UInt64;
var c: cardinal;
    P: PChar;
begin
  P := Pointer(S);
  if P=nil then begin
    result := 0;
    exit;
  end;
  if ord(P^) in [1..32] then repeat inc(P) until not(ord(P^) in [1..32]);
  c := ord(P^)-48;
  if c>9 then
    result := 0 else begin
    result := c;
    inc(P);
    repeat
      c := ord(P^)-48;
      if c>9 then
        break else
        result := result*10+c;
      inc(P);
    until false;
  end;
end;

It will work in both Unicode and not Unicode versions of Delphi.

On error, it returns 0.

Arnaud Bouchez
  • 42,305
  • 3
  • 71
  • 159
  • +1 from me too, but just one question. Won't this fail with 64-bit compiler due to the first `Pointer` cast ? Looks like a typo to me, don't you wanted to use `PChar` there instead ? – TLama Aug 04 '12 at 20:41
  • @TLama Why so? Pointer cast just the same for 32 and 64 bit compiler. Personally I'd use S[i] but that's mostly just preference. – David Heffernan Aug 04 '12 at 23:02
  • @David, yeah I realized that later on (but forgot to delete that comment...) – TLama Aug 04 '12 at 23:18
  • Returning zero on error isn't terribly useful. And it really ought to deal with out of range input. – David Heffernan Aug 05 '12 at 12:09
  • 2
    @DavidHeffernan I've several variants of such functions, one using an `var error: integer` additional parameter. Returning 0 does make sense depending on the context (for instance in our *mORMot* framework). It was just to show one implementation pattern. @TLama This code will work from Delphi 2 up to XE2, including 64 bit version of XE2, and also FPC, I think. – Arnaud Bouchez Aug 06 '12 at 07:58
3

According to the documentation,

S is a string-type expression; it must be a sequence of characters that form a signed real number.

I agree the documentation is a bit vague; indeed, what exactly does form mean, and exactly what is meant by a signed real number (especially if num is an integer type)?

Still, I think the part to highlight is signed. In this case, you want an integer, and so S must be a sequence of characters that form a signed integer. But then your maximum is High(Int64) = 9223372036854775807

Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • 4
    `Val` is an intrinsic that resolves to `System._ValInt64` when pass an 8 byte wide integer. And `_ValInt64` returns a signed integer. Documentation is pathetic. – David Heffernan Aug 04 '12 at 13:19
0
function TryStrToInt64(const S: string; out Value: Int64): Boolean;
var
  E: Integer;
begin
  Val(S, Value, E);
  Result := E = 0;
end;
0

The documentation on this really is lacking, but I use StrToUInt64 and UIntToStr from System.SysUtils, and they convert between strings and unsigned 64-bit integers.

I'm not sure when these were added to Delphi, but they are certainly in the last few releases.

Ron Maupin
  • 6,180
  • 4
  • 29
  • 36