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
?
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;
[...]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With