在 Delphi6 或 Delphi 2010 中,声明货币类型的两个变量 (vtemp1,vtemp2) 并为它们提供值 0.09。将其中一个变量嵌入到 ABS 函数中,并将其与另一个进行比较。由于编译器监视显示 abs(vtemp1) 和 vtemp2 的值相同,因此您会期望比较产生积极的结果。奇怪的是 if 语句失败了!!!
注意: - 只有在处理数字 0.09 时才会遇到此问题(尝试其他几个接近的值显示正常结果) - 将变量声明为 Double 而不是货币,问题不再存在。
在 Delphi6 或 Delphi 2010 中,声明货币类型的两个变量 (vtemp1,vtemp2) 并为它们提供值 0.09。将其中一个变量嵌入到 ABS 函数中,并将其与另一个进行比较。由于编译器监视显示 abs(vtemp1) 和 vtemp2 的值相同,因此您会期望比较产生积极的结果。奇怪的是 if 语句失败了!!!
注意: - 只有在处理数字 0.09 时才会遇到此问题(尝试其他几个接近的值显示正常结果) - 将变量声明为 Double 而不是货币,问题不再存在。
我认为原因是类型转换。Abs()
函数返回real
结果,因此currency
变量转换为real
. 看一下文档:
货币是一种定点数据类型,可最大限度地减少货币计算中的舍入误差。在 Win32 平台上,它存储为一个缩放的 64 位整数,最后四个有效数字隐式表示小数位。在赋值和表达式中与其他实数类型混合时,货币值会自动除以或乘以 10000。
所以货币是固定的,真实是浮点数。您的问题的示例代码是:
program Project3;
{$APPTYPE CONSOLE}
const VALUE = 0.09;
var a,b : currency;
begin
a := VALUE;
b := VALUE;
if a = Abs(b) then writeln('equal')
else writeln('not equal', a - Abs(b));
readln;
end.
由于类型转换,产生不相等的结果;
编译器观察显示 abs(vtemp1) 和 vtemp2 的值相同
尝试添加x : real
,然后调用x := abs(b);
,添加x
到手表列表,选择它并按Edit watch
,然后选择浮点。X
变成0.899...967
.
不仅0.09
价值会产生这样的结果。你可以试试这个代码来检查:
for i := 0 to 10000 do begin
a := a + 0.001;
b := a;
if a <> abs(b) then writeln('not equal', a);
end;
所以,如果您需要货币变量的绝对值 - 就去做吧。不要使用浮点数abs()
:
function Abs(x : Currency):Currency; inline;
begin
if x > 0 then result := x
else result := -x;
end;
一点澄清。如果比较浮点值,则会出现“问题”:
var
A: Currency;
begin
A:= 0.09;
Assert(A = Abs(A));
end;
那是因为Abs(A)
返回一个浮点值,并且A = Abs(A)
实现为浮点比较。
如果比较货币值,我无法重现它:
var
A, B: Currency;
begin
A:= 0.09;
B:= Abs(A);
Assert(A = B);
end;
但第二个示例也是一个潜在的错误,因为B:= Abs(A)
内部是浮点除法/乘以 10000 并舍入为货币 (int64),并且取决于 FPU 舍入模式。
我创建了一个qc 报告 #107893,它已打开。
我刚刚发现 Delphi XE2 Abs函数不会重载货币类型的困难方式。
这些是 abs 支持的唯一类型