1

I am reading a floating point attribute value from a node in an XML file with TXMLDocument:

<MyApp_Favorites version="1.0">

...with this statement:

var
  ThisRootNode: IXMLNode;
  ThisVersion: Single;

// ...

ThisVersion := ThisRootNode.Attributes['version'];
CodeSite.Send('ThisVersion', ThisVersion);

However, on my German language system I get this version value:

ThisVersion = 10,00

...as in my regional settings the comma "," is defined as decimal separator and not the dot "." as in the XML file. But with an English language regional setting - where the dot is most probably defined as decimal separator setting - the result will be correct as "1.0".

So how can I make sure that independently from the regional setting the read VALUE of 1.0 will be always the same? (Reading the version value as string and then convert it to float doesn't seem to be a very elegant method).

user1580348
  • 5,721
  • 4
  • 43
  • 105
  • Here we are again at malicious downvoting of my legitimate and well-formulated questions by some specific people without any explanations! – user1580348 Dec 19 '16 at 22:44

3 Answers3

5

Use the string representation and do the conversion yourself using TFormatSettings.Invariant:

ThisVersion := StrToFloat(ThisRootNode.Attributes['version'], TFormatSettings.Invariant);
Uwe Raabe
  • 45,288
  • 3
  • 82
  • 130
3

A floating point number isn't a sensible format for a version number, rounding errors and lack of representability being major drawbacks. Consider using a record with integers to define the version numbers.

program Project1;
{$APPTYPE CONSOLE}

uses
  SysUtils, StrUtils, Types;
type    
TVersion = record
  private
    procedure Set_AsString(AVersion : string);
    function Get_AsString: String;
  public
    Major: Integer;
    Minor: Integer;
    property AsString: String read Get_AsString write Set_AsString;
end;    

function TVersion.Get_AsString: String;
begin
  Result := Format('%d.%d', [Major, Minor]);
end;

procedure TVersion.Set_AsString(AVersion: string);
var
  split : TStringDynArray;
begin
  split := SplitString(AVersion, '.');
  Major := StrToInt(split[0]);
  Minor := StrToInt(split[1]);
end;

var
  v1, v2 : TVersion;
  s : string;
begin
  v1.Major := 12;
  v1.Minor := 34;
  WriteLn(v1.AsString);

  s := v1.AsString;
  v2.AsString := s;

  WriteLn(v2.AsString);
  ReadLn;
end.
J...
  • 30,968
  • 6
  • 66
  • 143
2

This value should not be treated as a floating point value. You open yourself up to issues with representation of binary floating point types. Far better is to read the string and parse out major and minor parts, converting them to integer with StrToInt. Split the string itself using Pos to find the location of the period separator, or indeed using any Split function.

On the other hand, it's not even clear that you need this value in any format other than a string. So why bother to do anything beyond reading it as a string?

As for the actual question you asked, should you ever wish to do that use the overload of StrToFloat that accepts a TFormatSettings parameter. And pass in format settings that set the decimal separator as '.'.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Well, maybe I could need in the future to make an arithmetical comparison of two or more version values. – user1580348 Dec 19 '16 at 14:58
  • Don't view this as a real value. It's not. It's two integers, major and minor version. – David Heffernan Dec 19 '16 at 15:01
  • @user1580348 You can do that with a record using [Operator Overloading](http://docwiki.embarcadero.com/RADStudio/Berlin/en/Operator_Overloading_(Delphi)#Declaring_Operator_Overloads) – J... Dec 19 '16 at 15:08
  • Enhanced record for a trivial task like this seems over the top. – David Heffernan Dec 19 '16 at 15:09
  • @DavidHeffernan I suppose, but it depends on how often it gets used. Adding an operator is also trivial and then you never need to worry about this or similar problems again. – J... Dec 19 '16 at 15:24