How to read a floating point value correctly from an XML file independently from regional settings?

1.6k views Asked by At

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).

3

There are 3 answers

2
J... On BEST ANSWER

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.
0
Uwe Raabe On

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

ThisVersion := StrToFloat(ThisRootNode.Attributes['version'], TFormatSettings.Invariant);
5
David Heffernan On

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 '.'.