mjn*_*mjn 18 delphi compare delphi-2009
当使用Delphi 2009编译并运行时,此控制台应用程序会写"奇怪"."小于"运算符两边的值相等,但代码的行为就好像它们不相等.我该怎么做才能避免这个问题?
program Project5;
{$APPTYPE CONSOLE}
var
C: Currency;
begin
C := 1.32;
if C < 1.32 then
begin
WriteLn('strange');
end;
ReadLn;
end.
Run Code Online (Sandbox Code Playgroud)
ps代码适用于其他值.
此答案由巴里·凯利解释说,货币型"是不容易的浮点代码是相同的方式精度的问题."
这似乎是德尔福的回归.
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');
Run Code Online (Sandbox Code Playgroud)
文字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');
Run Code Online (Sandbox Code Playgroud)
请注意,文字保存在4字节的浮点数中.我们可以通过比较来看出这一点dword ptr [$00406128].如果我们查看存储在$00406128我们发现的单精度浮点数的内容:
00 40 4E 46
这正好是13200,表示为4字节的浮点数.
我的猜测是2010年的编译器遇到以下情况时会做以下事情1.32:
$00405118.因为1.32不能完全表示,所以最终的10字节浮点数并不完全是13200.并且可能是当编译器从4字节浮点数中存储这些文字到将它们存储在10字节浮点数时进行回归.
根本问题在于Delphi对Currency数据类型的支持建立在一个完全有缺陷的设计上.使用二进制浮点算法实现十进制定点数据类型只是在寻找麻烦.修复设计的唯一理智方法是完全重新设计编译器以使用定点整数算法.值得注意的是,新的64位编译器使用与32位编译器相同的设计.
老实说,我会阻止Delphi编译器使用Currency文字进行任何浮点运算.这只是一个完整的雷区.我会像这样在我脑海里做10000次转变:
function ShiftedInt64ToCurrency(Value: Int64): Currency;
begin
PInt64(@Result)^ := Value;
end;
Run Code Online (Sandbox Code Playgroud)
然后调用代码将是:
C := 1.32;
if C < ShiftedInt64ToCurrency(13200) then
Writeln ('strange');
Run Code Online (Sandbox Code Playgroud)
编译器无法搞砸了!
哼!
由于无法进行诸如Currency(1.32)之类的强制转换,因此可以使用以下内容进行显式转换
Function ToCurrency(d:Double):Currency;
begin
Result := d;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
C: Currency;
begin
C := 1.32;
if C < ToCurrency(1.32) then
begin
Writeln ('strange');
end;
end;
Run Code Online (Sandbox Code Playgroud)
另一种方法可以通过使用const或变量来强制使用curreny
const
comp:Currency=1.32;
var
C: Currency;
begin
C := 1.32;
if C < comp then
begin
writeln ('strange');
end;
end;
Run Code Online (Sandbox Code Playgroud)