这似乎是德尔福的回归。
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。这是一个可精确表示的二进制浮点值。存储为 10 字节浮点数的 13200 的位模式为:
00 00 00 00 00 00 40 CE 0C 40
但是,存储在 $00405118 的文字中的位模式是不同的,并且略大于13200
. 值为:
01 00 00 00 00 00 40 CE 0C 40
这就解释了为什么C < 1.32
评估为True
.
在 XE2 上生成的代码是:
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 字节浮点数中。这可以从我们比较的事实中看出dword ptr [$00406128]
。如果我们查看存储在的单精度浮点数的内容,$00406128
我们会发现:
00 40 4E 46
这正是 13200 表示为 4 字节浮点数。
我的猜测是,2010 年的编译器在遇到以下问题时会执行以下操作1.32
:
- 将 1.32 转换为最接近的可精确表示的 10 字节浮点数。
- 将该值乘以 10000。
- 将生成的 10 字节浮点数存储在
$00405118
.
因为 1.32 不能完全表示,所以最终的 10 字节浮点数并不完全是 13200。并且推测当编译器从将这些文字存储在 4 字节浮点数中切换到将它们存储在 10 字节浮点数中时,就会出现回归。
根本问题是德尔福对Currency
数据类型的支持是建立在一个完全有缺陷的设计之上的。使用二进制浮点运算来实现十进制定点数据类型简直是自找麻烦。修复设计的唯一合理方法是完全重新设计编译器以使用定点整数运算。令人失望的是,新的 64 位编译器使用与 32 位编译器相同的设计。
老实说,我会阻止 Delphi 编译器对Currency
文字进行任何浮点运算。这只是一个完整的雷区。我会像这样在脑海中进行 10,000 次转变:
function ShiftedInt64ToCurrency(Value: Int64): Currency;
begin
PInt64(@Result)^ := Value;
end;
然后调用代码将是:
C := 1.32;
if C < ShiftedInt64ToCurrency(13200) then
Writeln ('strange');
编译器没有办法把它搞砸!
哼!