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

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

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

有帮助吗?

解决方案

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

但是,如果您将货币数字乘以浮点类型或除以货币值,则需要以某种方式计算舍入。 FPU控制这种机制(它被称为“控制字”)。 Math单元包含一些控制此机制的过程:特别是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.

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

此外,如果您的计算转移到普通浮动点然后再转换为货币,那么所有投注都将被取消 - 太难以审核。确保所有计算都是货币。

其他提示

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

比较两个 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编译器下)更快。

有关其他信息,请参见这篇文章

如果您的情况与我的情况相似,您可能会发现这种方法很有帮助。我主要在工资单上工作。如果一个企业说3个部门,并且想要在这三个部门之间平均收取员工的成本,那么很多时候会出现四舍五入的问题。

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

见线程:

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

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。

为避免德尔福货币四舍五入可能出现的问题,请使用4位小数。

这将确保您在进行非常少量的计算时不会出现舍入问题。

&quot;去过那里。完成那件事。写单元测试。“

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top