在Delphi中ABS功能失败

Joh*_*hny 6 delphi

在Delphi6或Delphi 2010中,声明两个Currency类型的变量(vtemp1,vtemp2),并为它们提供0.09的值.使用ABS功能嵌入其中一个变量并将其与另一个进行比较.当编译器监视显示abs(vtemp1)和vtemp2的相同值时,您可能希望比较产生正结果.奇怪的是if语句失败了!

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

ter*_*ran 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.
Run Code Online (Sandbox Code Playgroud)

由于类型转换,产生的结果不相同 ;

编译器监视显示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;
Run Code Online (Sandbox Code Playgroud)

所以,如果你需要货币变量的绝对值 - 就这么做吧.不要使用浮点数abs():

    function Abs(x : Currency):Currency; inline;
    begin
        if x > 0 then result := x
        else result := -x;
    end;
Run Code Online (Sandbox Code Playgroud)

  • @Khatchig - 正式地说,这不是一个错误,而是一个记录在案的行为("按设计").我同意这是一个不好的行为,Embarcadero可以修复它(让`Abs(货币)`返回货币类型,而不是浮动类型). (2认同)

klu*_*udg 6

一点澄清.如果比较浮动值,则会出现"问题":

var
  A: Currency;

begin
  A:= 0.09;
  Assert(A = Abs(A));
end;
Run Code Online (Sandbox Code Playgroud)

这是因为Abs(A)返回一个浮点值,并A = Abs(A)实现为浮点数比较.

如果比较货币值,我无法重现它:

var
  A, B: Currency;
begin
  A:= 0.09;
  B:= Abs(A);
  Assert(A = B);
end;
Run Code Online (Sandbox Code Playgroud)

但第二个样本也是一个潜在的错误,因为B:= Abs(A)内部是一个浮点除法/乘以10000,舍入为货币(int64),并取决于FPU舍入模式.


我创建了一个qc报告#107893,它被打开了.