5

When one assigns a Variant containing a string value to a floating point variable Delphi calls VarToDoubleAsString to do the conversion, which in turn uses the OS settings for decimal and thousand separator (via VarR8FromStr). This is problematic if one has to change SysUtils.DecimalSeparator and SysUtils.ThousandSeparator. For example run the following program:

program VarStrToFloat;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Math;

function FormatFloatUsingDelphiSettings(Value: Extended): string;
begin
  Result := FormatFloat('#,##0.00', Value);
end;

procedure Test(const AMsg: string);
var
  r1, r2: Extended;
  s1, s2: string;
  v: Variant;
begin
  r1 := 5432.1;
  s1 := FormatFloatUsingDelphiSettings(r1);
  v := s1; // <== conversion uses OS settings
  r2 := v;
  s2 := FormatFloatUsingDelphiSettings(r2);

  Write(AMsg: 8, s1: 10, s2: 10, '  ');
  if SameValue(r1, r2) then
    Writeln('OK')
  else
    Writeln('FAIL');
end;

procedure SwapEm;
var
  tmp: Char;
begin
  tmp := DecimalSeparator;
  DecimalSeparator := ThousandSeparator;
  ThousandSeparator := tmp;
end;

begin
  Test('Default');
  SwapEm;
  Test('Changed');
  Readln;
end.

The first test works fine, the second one fails.

Is there a way to make the Variant conversion use SysUtils.DecimalSeparator and SysUtils.ThousandSeparator?

Uli Gerhardt
  • 13,748
  • 1
  • 45
  • 83
  • 1
    I don't know any easy way to do this. There used to be a way to replace variant conversion routines (`SetVariantManager`), but this was removed in recent releases. Well, it's still there, it just does nothing. I need to replace `_VarFromCurr` for reasons which are too complex to explain in this comment (QC#87786). You could achieve what you need by replacing `_VarToDouble` which would be possible but rather more difficult (involves some hooking). Of course, there may be an official way to do this that I'm not aware of..... – David Heffernan Feb 23 '11 at 11:28

1 Answers1

12

You can replace the VarR8FromStr function in varutils.pas to your liking, and VarToDoubleAsString will use it instead:

function MyConversion(const strIn: WideString; LCID: Integer; dwFlags: Longint;
    out dblOut: Double): HRESULT; stdcall;
const
  CResult: array [False..True] of HRESULT = (VAR_INVALIDARG, VAR_OK);
var
  s: string;
begin
  s := StringReplace(StrIn, ThousandSeparator, '', [rfReplaceAll]);
  Result := CResult[TryStrToFloat(s, dblOut)];
end;

[...]

begin
  varutils.VarR8FromStr := MyConversion;
  [...]
Sertac Akyuz
  • 54,131
  • 4
  • 102
  • 169
  • +1 Nice! Didn't know this. Sadly doesn't help me with my `_VarFromCurr` problem!! – David Heffernan Feb 23 '11 at 12:17
  • @David - I saw the report.. It would be nice if `CmpTypeMap` in `VarCompareSimple` was replaceable or elements were assignable or something... Why can't you do `case TVarData(Value).VType of` `varDouble: if TVarData(Value).VDouble = 0.0 then`, no variants involved for the comparison. – Sertac Akyuz Feb 24 '11 at 01:30