Make VarToDoubleAsString use Delphi settings (not OS settings)
Asked Answered
A

1

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?

Airbrush answered 23/2, 2011 at 11:12 Comment(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.....Taddeusz
W
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;
  [...]
Wop answered 23/2, 2011 at 12:12 Comment(2)
+1 Nice! Didn't know this. Sadly doesn't help me with my _VarFromCurr problem!!Taddeusz
@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.Wop

© 2022 - 2024 — McMap. All rights reserved.