StrToFloat 无法报告 Delphi 64 位中的无效浮点数
StrToFloat fails to report invalid floating point numbers in Delphi 64bits
以下代码尝试转换超出双精度范围的值
StrToFloat('1e99999999')
使用 Windows 32 位编译器在 Delphi 10.2r3 中正确地报告了一个不正确的浮点值,但是当使用 Window 64 位编译器编译时,它会默默地 returns 一个 0(零)。
有没有办法让StrToFloat在浮点值不正确的时候报错?
我试过TArithmeticException.exOverflow,但在那种情况下没有效果。
我也试过TArithmeticException.exPrecision,但它在许多常见的近似情况下触发(f.i。它在转换'1e9'时触发)。
在 Delphi 10.2 更新 3
中发现了问题
附录: 为了解决这个问题,我已经开始了字符串到双重转换的洁净室替代实现,带有测试的初始版本可以在 dwscript commit 2ba1d4a 中找到
这是使用 PUREPASCAL 版本 StrToFloat
的所有 Delphi 版本中存在的缺陷。这映射到 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;
问题是
的位置
if Result > CMaxExponent then
这个测试应该在循环内部,并且在这段代码的 asm x86 版本中。如上所述,在循环外进行最大指数测试,16 位有符号整数结果值对于 99999999
的指数来说太小了。读取指数时,Result
中的值溢出,变为负值。因此,对于您的示例,事实证明使用了指数 -7937
而不是 99999999
。这自然会导致值为零。
这是一个明显的错误,我已经提交了错误报告:RSP-20333。
至于如何解决这个问题,我不知道 Delphi RTL 中的另一个函数可以执行此任务。所以我认为您需要执行以下操作之一:
- 自己滚动
StrToFloat
。
- Pre-process 字符串,并在读取
StrToFloat
. 之前处理超出范围的指数
- 使用 C 运行时库中执行相同任务的函数之一。
最后,非常感谢你提出这个问题,因为我知道我自己的程序受到了这个缺陷的影响,所以我现在可以修复它了!
更新:
您可能还有兴趣查看我在调查时发现的相关错误:RSP-20334. It might surprise you to realise that, StrToFloat('߀')
, when using the PUREPASCAL version of StrToFloat
, returns 1936.0
. The trick is that the character that is being passed to StrToFloat
is a non-Latin digit, in this case U+07C0。
以下代码尝试转换超出双精度范围的值
StrToFloat('1e99999999')
使用 Windows 32 位编译器在 Delphi 10.2r3 中正确地报告了一个不正确的浮点值,但是当使用 Window 64 位编译器编译时,它会默默地 returns 一个 0(零)。
有没有办法让StrToFloat在浮点值不正确的时候报错?
我试过TArithmeticException.exOverflow,但在那种情况下没有效果。
我也试过TArithmeticException.exPrecision,但它在许多常见的近似情况下触发(f.i。它在转换'1e9'时触发)。
在 Delphi 10.2 更新 3
中发现了问题附录: 为了解决这个问题,我已经开始了字符串到双重转换的洁净室替代实现,带有测试的初始版本可以在 dwscript commit 2ba1d4a 中找到
这是使用 PUREPASCAL 版本 StrToFloat
的所有 Delphi 版本中存在的缺陷。这映射到 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;
问题是
的位置if Result > CMaxExponent then
这个测试应该在循环内部,并且在这段代码的 asm x86 版本中。如上所述,在循环外进行最大指数测试,16 位有符号整数结果值对于 99999999
的指数来说太小了。读取指数时,Result
中的值溢出,变为负值。因此,对于您的示例,事实证明使用了指数 -7937
而不是 99999999
。这自然会导致值为零。
这是一个明显的错误,我已经提交了错误报告:RSP-20333。
至于如何解决这个问题,我不知道 Delphi RTL 中的另一个函数可以执行此任务。所以我认为您需要执行以下操作之一:
- 自己滚动
StrToFloat
。 - Pre-process 字符串,并在读取
StrToFloat
. 之前处理超出范围的指数
- 使用 C 运行时库中执行相同任务的函数之一。
最后,非常感谢你提出这个问题,因为我知道我自己的程序受到了这个缺陷的影响,所以我现在可以修复它了!
更新:
您可能还有兴趣查看我在调查时发现的相关错误:RSP-20334. It might surprise you to realise that, StrToFloat('߀')
, when using the PUREPASCAL version of StrToFloat
, returns 1936.0
. The trick is that the character that is being passed to StrToFloat
is a non-Latin digit, in this case U+07C0。