6

在 Delphi6 或 Delphi 2010 中,声明货币类型的两个变量 (vtemp1,vtemp2) 并为它们提供值 0.09。将其中一个变量嵌入到 ABS 函数中,并将其与另一个进行比较。由于编译器监视显示 abs(vtemp1) 和 vtemp2 的值相同,因此您会期望比较产生积极的结果。奇怪的是 if 语句失败了!!!

注意: - 只有在处理数字 0.09 时才会遇到此问题(尝试其他几个接近的值显示正常结果) - 将变量声明为 Double 而不是货币,问题不再存在。

4

3 回答 3

17

我认为原因是类型转换。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;
于 2012-08-13T08:10:06.827 回答
6

一点澄清。如果比较浮点值,则会出现“问题”:

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,它已打开。

于 2012-08-13T11:01:41.797 回答
2

我刚刚发现 Delphi XE2 Abs函数不会重载货币类型的困难方式。

请参阅Delphi XE2 文档维基

这些是 abs 支持的唯一类型

  • 函数 Abs(X: ):实数;超载;
  • 函数 Abs(X: ): Int64; 超载;
  • 函数 Abs(X: ):整数;超载;
于 2013-11-28T16:58:53.517 回答