zig
zig

Reputation: 4624

Does the Val procedure use DecimalSeparator?

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?

Upvotes: 7

Views: 1317

Answers (1)

Rudy Velthuis
Rudy Velthuis

Reputation: 28806

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.

UPDATE

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.

Upvotes: 8

Related Questions