在Delphi中比较货币值时如何避免舍入问题?
另外,Delphi Win32中的货币类型取决于处理器浮点精度。正因为如此,我在比较两个货币值时遇到舍入问题,根据机器返回不同的结果 现在我使用SameValue函数传递一个Epsilon参数=0.009,因为我只需要2位小数精度在Delphi中比较货币值时如何避免舍入问题?,delphi,winapi,floating-point,currency,Delphi,Winapi,Floating Point,Currency,另外,Delphi Win32中的货币类型取决于处理器浮点精度。正因为如此,我在比较两个货币值时遇到舍入问题,根据机器返回不同的结果 现在我使用SameValue函数传递一个Epsilon参数=0.009,因为我只需要2位小数精度 有没有更好的方法来避免这个问题?要避免Delphi中货币舍入可能出现的问题,请使用4位小数 这将确保在使用非常小的金额进行计算时,不会出现舍入问题 “在那里。完成了。编写了单元测试。”不,货币不是浮点类型。它是一个固定精度的十进制数,用整数存储实现。它可以精确地进行比
有没有更好的方法来避免这个问题?要避免Delphi中货币舍入可能出现的问题,请使用4位小数 这将确保在使用非常小的金额进行计算时,不会出现舍入问题
“在那里。完成了。编写了单元测试。”
不,货币不是浮点类型。它是一个固定精度的十进制数,用整数存储实现。它可以精确地进行比较,并且不存在舍入问题,比如说,双倍舍入。因此,如果您在货币变量中看到不精确的值,问题不在于货币类型本身,而在于您在其中输入了什么。最有可能的是,在代码的其他地方有一个浮点计算。因为您没有显示该代码,所以很难在这个问题上提供更多帮助。但是,一般来说,解决方案是在存储货币变量之前将浮点数四舍五入到正确的精度,而不是对货币变量进行不精确的比较。Delphi中的货币类型是按1/10000比例缩放的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.
您正在使用的第三方库可能正在将控制字设置为其他值。您可能希望在重要计算的起点显式设置控制字(即舍入模式)
此外,如果您的计算转换为普通浮点值,然后再转换为货币,那么所有的赌注都是无效的——难以审核。确保所有的计算都是货币。如果你的情况和我的一样,你可能会发现这种方法很有用。我主要在工资部工作。如果一个企业有三个部门,并且希望在这三个部门中平均收取一名员工的成本,那么很多时候会出现舍入问题 我一直在做的是循环各部门,每个部门收取总成本的三分之一,并将收取的成本添加到小计(货币)变量中。但是当循环变量等于极限值时,我没有乘以分数,而是从总成本中减去小计变量,并将其放入最后一个部门。由于这一过程产生的日记账分录必须始终保持平衡,因此我相信它始终是有效的。请参阅线程: D7/DUnit:所有CheckEquals(货币、货币)测试突然失败 看来,我们的开发工作站的变化导致了货币比较的失败。我们还没有找到根本原因,但在两台运行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的依赖性。比较两个
货币
值的更快、更安全的方法肯定是将变量映射到其内部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编译器下)更快
有关更多信息,请参阅。来自Delphi帮助:货币类型:8字节(64位)货币编号存储为缩放和有符号的64位整数,四个最低有效位隐式表示四位小数。两个货币值相乘使用fpu中的浮点指令完成。结果容易出现舍入错误。哇。我真的写了这个。试图删除此项,但它作为一个示例说明了不应做的事情。;)请注意,货币类型之间的算术运算是使用浮点指令完成的。由于浮点二进制表示,结果容易出现舍入错误。