4

AFAIK,Delphi Win32 中的货币类型取决于处理器浮点精度。因此,我在比较两个货币值时遇到了舍入问题,根据机器返回不同的结果。

现在我使用传递 Epsilon 参数 = 0.009 的 SameValue 函数,因为我只需要 2 个十进制数字精度。

有没有更好的方法来避免这个问题?

4

6 回答 6

15

Delphi 中的 Currency 类型是按 1/10,000 缩放的 64 位整数;换句话说,它的最小增量等于 0.0001。它不像浮点代码那样容易受到精度问题的影响。

但是,如果您将货币数字乘以浮点类型,或将货币值相除,则确实需要以一种或另一种方式计算舍入。FPU 控制这个机制(它被称为“控制字”)。数学单元包含一些控制这种机制的过程:特别是 SetRoundMode。你可以在这个程序中看到效果:

{$APPTYPE CONSOLE}

uses Math;

var
  x: Currency;
  y: Currency;
begin
  SetRoundMode(rmTruncate);
  x := 1;
  x := x / 6;
  SetRoundMode(rmNearest);
  y := 1;
  y := y / 6;
  Writeln(x = y); // false
  Writeln(x - y); // 0.0001; i.e. 0.1666 vs 0.1667
end.

您正在使用的第三方库可能将控制字设置为不同的值。您可能希望在重要计算的起点明确设置控制字(即舍入模式)。

此外,如果您的计算曾经转换为普通浮点数,然后又转换回货币,那么所有的赌注都会被取消 - 太难审计了。确保您的所有计算都使用货币。

于 2008-10-08T13:43:44.637 回答
12

不,货币不是浮点类型。它是一个固定精度的小数,用整数存储实现。它可以精确比较,并且不存在例如 Double 的舍入问题。因此,如果您在 Currency 变量中看到不精确的值,则问题不在于 Currency 类型本身,而在于您放入其中的内容。很可能,您的代码中的其他地方有一个浮点计算。由于您没有显示该代码,因此很难在这个问题上提供更多帮助。但一般来说,解决方案是在存储到 Currency 变量之前将浮点数四舍五入到正确的精度,而不是对 Currency 变量进行不精确的比较。

于 2008-10-08T13:34:40.163 回答
2

比较两个currency值的更快和更安全的方法当然是将变量映射到它们的内部Int64表示:

function CompCurrency(var A,B: currency): Int64;
var A64: Int64 absolute A;
    B64: Int64 absolute B;
begin
  result := A64-B64;
end;

这将避免比较期间的任何舍入错误(使用 *10000 整数值),并且将比默认的基于 FPU 的实现更快(尤其是在 64 位 XE2 编译器下)。

有关其他信息,请参阅本文

于 2011-11-09T07:07:15.940 回答
0

如果您的情况和我一样,您可能会发现这种方法很有帮助。我主要在工资单上工作。如果一个企业有3个部门,想在这三个部门之间平均收取员工的费用,很多时候都会出现四舍五入的问题。

我一直在做的是循环各部门收取总成本的三分之一并将收取的成本添加到小计(货币)变量中。但是当循环变量等于限制时,我不是乘以分数,而是从总成本中减去小计变量并将其放在最后一个部门。由于这个过程产生的日记帐分录总是需要平衡,我相信它总是有效的。

于 2008-10-08T15:06:46.997 回答
0

见线程:

D7 / DUnit:所有 CheckEquals(Currency, Currency) 测试突然失败......

https://forums.codegear.com/thread.jspa?threadID=16288

看起来我们的开发工作站上的更改导致货币比较失败。我们还没有找到根本原因,但是在两台运行 Windows 2000 SP4 的计算机上,并且独立于 gds32.dll 的版本(InterBase 7.5.1 或 2007)和 Delphi(7 和 2009),这一行

TIBDataBase.Create(nil);

现在将 8087 控制字的值从 $1372 更改为 $1272。

并且单元测试中的所有货币比较都会失败并显示有趣的消息,例如

Expected: <12.34> - Found: <12.34>

gds32.dll 没有被修改,所以我猜这个库中存在对修改控制字的第三方 dll 的依赖。

于 2009-05-06T15:48:55.327 回答
-2

为了避免在 Delphi 中可能出现的货币四舍五入问题,请使用 4 位小数。

这将确保您在进行非常少量的计算时永远不会遇到舍入问题。

"Been there. Done That. Written the unit tests."

于 2008-10-08T12:34:58.240 回答