StrToFloat无法在Delphi 64位中报告无效的浮点数

Eri*_*nge 22 delphi 64-bit delphi-10.2-tokyo

以下代码尝试将值转换为远超出双精度范围

StrToFloat('1e99999999')
Run Code Online (Sandbox Code Playgroud)

使用Windows 32位编译器在Delphi 10.2r3中正确报告错误的浮点值,但是当使用Window 64位编译器编译时,它会静默返回0(零).

当浮点值不正确时,有没有办法让StrToFloat报告错误?

我已经尝试过TArithmeticException.exOverflow,但在这种情况下这没有效果.

我也尝试过TArithmeticException.exPrecision,但它在许多通常的近似情况下触发(在转换'1e9'时触发它).

Delphi 10.2 update 3注意到了这个问题

附录:要解决这个问题,我已经启动了一个洁净室替代实现字符串到双重转换,初始版本带有测试可以在dwscript commit 2ba1d4a中找到

Dav*_*nan 23

这是使用PUREPASCAL版本的所有Delphi版本中存在的缺陷StrToFloat.映射到InternalTextToExtended哪个读取指数如下:

function ReadExponent: SmallInt;
var
  LSign: SmallInt;
begin
  LSign := ReadSign();
  Result := 0;
  while LCurrChar.IsDigit do
  begin
    Result := Result * 10;
    Result := Result + Ord(LCurrChar) - Ord('0');
    NextChar();
  end;

  if Result > CMaxExponent then
    Result := CMaxExponent;

  Result := Result * LSign;
end;
Run Code Online (Sandbox Code Playgroud)

问题是的位置

if Result > CMaxExponent then
Run Code Online (Sandbox Code Playgroud)

这个测试是在循环内部,在这个代码的asm x86版本中.如上所述,使用循环外的最大指数测试,16位有符号整数结果值对于指数而言太小99999999.读取指数时,Result溢出的值变为负值.因此,对于您的示例,事实证明使用了指数-7937而不是99999999.当然,这导致零值.

这是一个明显的错误,我已经提交了一个错误报告:RSP-20333.

至于如何解决问题,我不知道Delphi RTL中执行此任务的另一个功能.所以我认为你需要做以下其中一件事:

  • 滚动你自己StrToFloat.
  • 预处理字符串,并在读取之前处理超出范围的指数StrToFloat.
  • 使用执行相同任务的C运行时库中的一个函数.

最后,我很感谢您提出这个问题,因为我可以看到我自己的程序受到这个缺陷的影响,所以我现在可以解决它!

更新:

您可能还有兴趣查看我在调查时发现的相关错误:RSP-20334.您可能会意识到StrToFloat('?'),当使用PUREPASCAL版本时StrToFloat,会返回1936.0.诀窍是传递给的字符StrToFloat是非拉丁数字,在这种情况下是U + 07C0.