0

After some play with versions, I've encountered problem with data conversion.

I'm using GetVersionNumbers method to get version parts as MS and LS. Both output variables are Cardinal type. Now I have problem - I want to compare them to some other versions, but last ones are stored in Single (for example, 1.3). So, I'd like to convert either Single to Cardinal or Cardinal to Single (e.g. for 1.3 in Single, corresponding Cardinal is 65539).

As for now, I've found some interesting and simple Delphi solution, which relies on direct memory access:

function FloatToCardinal(val: Single): Cardinal;
var
p: ^Cardinal;          
begin
  p:=@(val);
  Result:=p^;
end;

The issue here is that Inno Setup apparently doesn't work with pointers this way - it gives "Identifier expected" error for p: ^Cardinal; part while trying to compile. So, I would like either to adapt this function for Inno Setup, or find some another way of conversion or comparison.

Would appreciate any help in this question.

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
lentinant
  • 792
  • 1
  • 10
  • 36
  • You just cannot reliably compare a float to a fixed value. That's not what floats are for. See [How should I do floating point comparison?](http://stackoverflow.com/q/4915462/850848) and many others. ... You should not store a version number as a float. That's just wrong. Floats are for mathematical calculations, nothing else. What makes you store version number as float? – Martin Prikryl Sep 19 '16 at 10:48
  • I'm not using float for comparison. I'm using Single, only because it is easy way to store part of version ("minor-major" or "rev-build"). I need to compare version of previously installed app to some hardcoded value, and I want to use Cardinal values to do that. I might use Cardinal values as hardcoded, but 1.3 is much more readable and understandable than 65539. So, for now, I was thinking about storing version parts as Single, converting them to Cardinal, and only then comparing. – lentinant Sep 19 '16 at 10:55
  • So the `1.3` is hardcoded? Are you doing something like `if Version > 1.3 then`? – Martin Prikryl Sep 19 '16 at 11:27
  • No, I've said already, I have no intention to use 1.3 for comparison. I want to convert it to Cardinal. Like `if (Version > FloatToCardinal(1.3)) then`. If I understand correctly, `GetVersionNumbers` returns Cardinal, which is, basically, representation of float (e,g. 1.3), converted to binary and than to Cardinal. – lentinant Sep 19 '16 at 12:58
  • What's the same thing. The conversion is the problem. – Martin Prikryl Sep 19 '16 at 14:12

1 Answers1

0

You cannot use floats for comparison reliably. Version number is not a (float) number. It's a sequence of integer numbers. You can even have a version like 1.2.3, you cannot even represent that as a float.


You better implement some handy utility function like:

function AreVersionNumbersAtLeast(
  Major, Minor, MajorAtLeast, MinorAtLeast: Cardinal): Boolean;
begin
  Result :=
    (Major > MajorAtLeast) or
    ((Major = MajorAtLeast) or (Minor >= MinorAtLeast));
end;

And use it like:

if AreVersionNumbersAtLeast(Major, Minor, 1, 3) then
  Log('Program is 1.3 or newer');

If you commonly use the MS returned by the GetVersionNumbers, you can wrap it to:

function IsVersionMSAtLeast(VersionMS, MajorAtLeast, MinorAtLeast: Cardinal): Boolean;
var
  Major: Cardinal;
  Minor: Cardinal;
begin
  Major := VersionMS shr 16;
  Minor := VersionMS and $FFFF;

  Result := AreVersionNumbersAtLeast(Major, Minor, MajorAtLeast, MinorAtLeast);
end;

And use it like:

if IsVersionMSAtLeast(VersionMS, 1, 3) then
  Log('Program is 1.3 or newer');

Or even a shorthand for a specific file like:

function IsFileVersionAtLeast(
  FileName: string; MajorAtLeast, MinorAtLeast: Integer): Boolean;
var
  VersionMS, VersionLS: Cardinal;
begin
  Result := 
    GetVersionNumbers(FileName, VersionMS, VersionLS) and
    IsVersionMSAtLeast(VersionMS, MajorAtLeast, MinorAtLeast);
end;

And use it like:

if IsFileVersionAtLeast(ExpandConstant('{app}\MyProg.exe'), 1, 3) then
  Log('Program is 1.3 or newer');

If you prefer the 1.3 syntax, use strings, not floats.

Parse the string with a function like:

function VersionStrToNumbers(Version: string; var Major, Minor: Cardinal): Boolean;
var
  V1, V2: Integer;
  P: Integer;
begin
  P := Pos('.', Version);
  Result := (P > 0);
  if Result then
  begin
    V1 := StrToIntDef(Copy(Version, 1, P - 1), -1);
    V2 := StrToIntDef(Copy(Version, P + 1, Length(Version) - P), -1);
    Result := (V1 >= 0) and (V2 >= 0);
    if Result then
    begin
      Major := Cardinal(V1);
      Minor := Cardinal(V2);
    end;
  end;
end;

And use it to implement a handy function like:

function IsFileVersionAtLeastStr(FileName, Version: string): Boolean;
var
  MajorAtLeast, MinorAtLeast: Cardinal;
begin
  Result :=
    VersionStrToNumbers(Version, MajorAtLeast, MinorAtLeast) and
    IsFileVersionAtLeast(FileName, MajorAtLeast, MinorAtLeast);
end;

And use it like:

if IsFileVersionAtLeast(ExpandConstant('{app}\MyProg.exe'), '1.3') then
  Log('Program is 1.3 or newer');

(This is just a conceptual code, I didn't test any of these)


Another approach is to calculate the "MS" and compare that:

function Version(Major, Minor: Cardinal): Cardinal;
begin
  Result := (Major shl 16) + Minor;
end;

Use it like:

if VersionMS >= Version(1, 3) then
  Log('Program is 1.3 or newer');
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
  • Thanks for the answer. I hope someone will find it useful. Just to note - apparently, `GetVersionNumbers` gets hex values, not float ones, like $10003 instead of 1.3. – lentinant Sep 19 '16 at 15:01
  • Thanks for undeleting the question. – Martin Prikryl Sep 19 '16 at 15:03
  • Sure, the `GetVersionNumbers` return an integers, not floats (hex is a just a representation of an integer, you cannot say that "function returns a hex"). If you like that, you can of course write `if VersionMS >= $10003`. Though beware that once you get to numbers like 1.10, it stops being obvious. You better use a function like `Version()`, which I just added to my answer. – Martin Prikryl Sep 19 '16 at 15:12