【问题标题】:Does the Val procedure use DecimalSeparator?Val 过程是否使用 DecimalSeparator?
【发布时间】:2017-12-24 10:46:53
【问题描述】:

StrToFloat 使用格式设置的DecimalSeparator

似乎Val 只接受包含. 作为小数分隔符的字符串。

_ValExtVal 调用)中的 ASM 代码看来,它没有使用 DecimalSeparator

我可以安全地相信Val 接受以. 作为小数分隔符的实数字符串这一事实吗?

【问题讨论】:

  • Delphi 的哪个版本? Val 是古老的,没有类型安全,需要手动错误检查......为什么不直接使用 StrToFloat 的两个参数重载并定义您需要的任何格式?这就是它的用途,而且它的可读性和可维护性都大大提高。
  • @J...,在 2009 年 5 月 7 日测试。我也需要它在 D5 中工作。
  • 对于没有两个参数重载的旧版本,我可能仍然更喜欢写类似的东西。除非这是性能瓶颈并且Val 提供了关键优化,否则我个人会避免它,至少对于生产代码而言。
  • @J...,我需要存储一个旧的DecimalSeparator;将 DecimalSeparator 设置为 '.' ;致电StrToFloat 并恢复DecimalSeparator。不是很好的 IMO,也不是线程安全的。就我而言,我知道输入字符串始终包含“。”作为分隔符。
  • @David Heffernan,为什么是 Delphi-5 标签? Val 不是对所有版本都一样吗?事实上,像TFormatSettings 这样的更好的选择并没有改变我提出的问题:Does the Val procedure use DecimalSeparator?

标签: delphi delphi-5


【解决方案1】:

Val 是古老的、低级的并且使用起来有点棘手。我不建议在用户代码中使用它。而是使用其他例程来扫描值,例如 StrToFloat 等。如果您将 StrToFloatTFormatSettings.Invariant 一起使用,则可以确保将点 ('.') 作为小数分隔符。

看看下面的一段测试代码。在我的德语系统上,小数点分隔符是逗号。所以我尝试了以下方法:

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;

输出是:

1.234 Extended:  1.23400000000000E+0000
1,234 Extended: Error, code = 2
1.234 Single:  1.23399996757507E+0000
Integer: 1234

这清楚地表明Val 不使用系统定义的小数分隔符,而只接受不变的小数分隔符,即'.'System.Val 的文档在这里有点误导,IMO。

更新

似乎我在代码的“单个部分”中使用了 E 而不是 S。显然,如果您传递Single,您也会得到正确的值,所以我猜编译器(它知道传递了什么)以某种方式将此信息传递给内部例程。

查看CPU窗口可以看到,如果传入一个浮点类型,则调用System.@ValExt,它返回FPU栈顶的值(ST(0))。然后编译器添加适当的代码来存储该值(FSTP TBYTEFSTP QWORDFSTP DWORD 分别用于 ExtendedDoubleSingle)。

类似地,对于整型变量(最多 32 位),调用 System.@ValLong,它在 EAX 中返回一个 Integer,并添加适当的代码以存储正确大小的值编译器。对于 64 位整数,调用@ValInt64,返回EDX:EAX 中的值。

FWIW,它还表明 Writeln 不使用系统定义的小数分隔符。

【讨论】:

  • 看看Single的代码。你得到了一个值,但是是错误的。我猜Val不知道你传递给它什么样的变量,只是将Extended 写入传入的地址。如果可以避免,请不要使用Val
  • 真正有用的地方很少
  • 不,这不是标准。默认的实数值类型是 Double。无论如何,扩展只存在于 x86 上。
  • 他们不是真的。因为在 x86 上,返回值通过 fpu 寄存器传输,所以使用什么类型并不重要。在所有其他平台上,扩展是双倍的。在任何情况下,Emba 都不是如何编写浮点库代码的典范。不要将他们的任何浮点库代码视为良好实践。例如,他们有没有修复过 Sinh 和 Cosh? qc.embarcadero.com/wc/qcmain.aspx?d=108802
  • 因此,浮点函数返回 Extended 但您不想声明扩展变量并不重要。如果您关心性能,甚至想要定位到 x86 以外的平台,则不需要。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-11-05
  • 2022-06-11
  • 1970-01-01
  • 2014-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多