StrToFloat
uses the DecimalSeparator
of the format settings.
It seems that Val
only accepts strings that contains .
as decimal separator.
From the ASM code in _ValExt
(which Val
calls) it looks like it does not use DecimalSeparator
.
Can I safely rely on the fact (?) that Val
accepts real number strings with .
as decimal separator?
Val converts the value represented in the string S to a numerical value or an enumerated value, and stores this value in the variable V , which can be of type Longint , Real and Byte or any enumerated type.
Description. The Val procedure is an older Delphi procedure that can convert a string NumberString into both integer and floating point variables. The variable NumberVar must be appropriate to the number string. Specifically, an integer string value must be provided when NumberVar is an Integer type.
The Val function converts the specified string expression to a number and returns it as a double. The returned value is forced to the data type of the assigned variable. The Val function reads characters in the string up to the first non-numeric character.
Val
is ancient, low level and a bit tricky to use. I would not recommend using it in user code. Rather use other routines to scan values, like StrToFloat
, etc. If you use StrToFloat
with TFormatSettings.Invariant
, you can be sure that you get the dot ('.'
) as decimal separator.
Take a look at the following piece of test code. On my German system, the decimal separator is a comma. So I tried the following:
procedure Test;
var
E: Extended;
S: Single;
I: Integer;
Code: Integer;
begin
Val('1.234', E, Code);
if Code = 0 then
Writeln('1.234 Extended: ', E)
else
Writeln('1.234 Extended: Error, code = ', Code);
Val('1,234', E, Code);
if Code = 0 then
Writeln('1,234 Extended: ', E)
else
Writeln('1,234 Extended: Error, code = ', Code);
Val('1.234', S, Code);
if Code = 0 then
Writeln('1.234 Single: ', S)
else
Writeln('1.234 Single: Error, code = ', Code);
Val('1234', I, Code);
if Code = 0 then
Writeln('Integer: ', I)
else
Writeln('Integer: Error, code = ', Code);
end;
The output is:
1.234 Extended: 1.23400000000000E+0000
1,234 Extended: Error, code = 2
1.234 Single: 1.23399996757507E+0000
Integer: 1234
This clearly demonstrates that Val
does not use the system-defined decimal separator, and only accepts the invariant decimal separator, i.e. '.'
. The docs for System.Val
are a little misleading here, IMO.
Seems I used E
instead of S
in the "single part" of the code. Apparently you also get the correct value if you pass a Single
, so I guess the compiler (which knows what gets passed) somehow passes this information to the internal routine.
Looking at the CPU window, you can see that if a floating point type is passed in, System.@ValExt
is called, which returns the value on the top of the FPU stack (ST(0)
). The compiler than adds the appropriate code to store that value (FSTP TBYTE
, FSTP QWORD
or FSTP DWORD
for Extended
, Double
and Single
, respectively).
Similarly, for integral variables (up to 32 bit), System.@ValLong
is called, which returns an Integer
in EAX
, and appropriate code to store the value in the right size is added by the compiler. For 64 bit integers, @ValInt64
is called, which returns a value in EDX:EAX
.
FWIW, it also shows that Writeln
doesn't use the system-defined decimal separator.
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