gpt4 book ai didi

delphi - 货币值(value)/恒定值比较的奇怪结果

转载 作者:行者123 更新时间:2023-12-03 14:35:16 25 4
gpt4 key购买 nike

当用 Delphi 2009 编译并运行时,这个控制台应用程序会写“奇怪”。“小于”运算符两侧的值相等,但代码的行为就像它们不相等一样。我该怎么做才能避免这个问题?

program Project5;

{$APPTYPE CONSOLE}

var
C: Currency;
begin
C := 1.32;

if C < 1.32 then
begin
WriteLn('strange');
end;

ReadLn;
end.

附:代码与其他值一起工作正常。

这个answer Barry Kelly 解释说,货币类型“不像浮点代码那样容易受到精度问题的影响。”

最佳答案

这似乎是 Delphi 中的回归。

Delphi 2010 中的输出是“奇怪的”。但在 XE2 中没有输出,因此不存在该错误。我手头没有 XE 可供测试,但感谢 @Sertac 确认 XE 也输出“奇怪”。请注意,旧版本的 Delphi 也可以,因此这是 D2009 左右时间的回归。

2010 年生成的代码是:

Project106.dpr.10: if C < 1.32 then
004050D6 DB2D18514000 fld tbyte ptr [$00405118]
004050DC DF2D789B4000 fild qword ptr [$00409b78]
004050E2 DED9 fcompp
004050E4 9B wait
004050E5 DFE0 fstsw ax
004050E7 9E sahf
004050E8 7319 jnb $00405103
Project106.dpr.12: WriteLn('strange');

文字 1.32 存储为 10 字节浮点值,其值为 13200。这是一个可精确表示的二进制浮点值。 13200 存储为 10 字节 float 的位模式为:

00 00 00 00 00 00 40 CE 0C 40

However, the bit pattern stored in the literal at $00405118 is different, and is slightly greater than 13200. The value is:

01 00 00 00 00 00 40 CE 0C 40

And that explains why C < 1.32 evaluates to True.

On XE2 the code generated is:

Project106.dpr.10: if C < 1.32 then
004060E6 DF2DA0AB4000 fild qword ptr [$0040aba0]
004060EC D81D28614000 fcomp dword ptr [$00406128]
004060F2 9B wait
004060F3 DFE0 fstsw ax
004060F5 9E sahf
004060F6 7319 jnb $00406111
Project106.dpr.12: WriteLn('strange');

请注意,文字保存在 4 字节 float 中。通过与 dword ptr [$00406128] 进行比较就可以看出这一点。 。如果我们查看存储在 $00406128 的单精度 float 的内容我们发现:

00 40 4E 46

And that is exactly 13200 as represented as a 4 byte float.

My guess is that the compiler in 2010 does the following when faced with 1.32:

  • Convert 1.32 to the nearest exactly representably 10 byte float.
  • Multiply that value by 10000.
  • Store the resulting 10 byte float away at $00405118.

Because 1.32 is not exactly representable, it turns out that the final 10 byte float is not exactly 13200. And presumably the regression came about when the compiler switch from storing these literals in 4 byte floats to storing them in 10 byte floats.

The fundamental problem is that Delphi's support for the Currency data type is founded on an utterly flawed design. Using binary floating point arithmetic to implement a decimal fixed point data type is simply asking for trouble. The only sane way to fix the design would be to completely re-engineer the compiler to use fixed point integer arithmetic. It's rather disappointing to note that the new 64 bit compiler uses the same design as the 32 bit compiler.

To be quite honest with you, I would stop the Delphi compiler doing any floating point work with Currency literals. It's just a complete minefield. I would do the 10,000 shift in my head like this:

function ShiftedInt64ToCurrency(Value: Int64): Currency;
begin
PInt64(@Result)^ := Value;
end;

然后调用代码将是:

C := 1.32;
if C < ShiftedInt64ToCurrency(13200) then
Writeln ('strange');

编译器没有办法把它搞砸!

哼!

关于delphi - 货币值(value)/恒定值比较的奇怪结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14358407/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com