Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/lua/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Delphi中比较货币值时如何避免舍入问题?_Delphi_Winapi_Floating Point_Currency - Fatal编程技术网

在Delphi中比较货币值时如何避免舍入问题?

在Delphi中比较货币值时如何避免舍入问题?,delphi,winapi,floating-point,currency,Delphi,Winapi,Floating Point,Currency,另外,Delphi Win32中的货币类型取决于处理器浮点精度。正因为如此,我在比较两个货币值时遇到舍入问题,根据机器返回不同的结果 现在我使用SameValue函数传递一个Epsilon参数=0.009,因为我只需要2位小数精度 有没有更好的方法来避免这个问题?要避免Delphi中货币舍入可能出现的问题,请使用4位小数 这将确保在使用非常小的金额进行计算时,不会出现舍入问题 “在那里。完成了。编写了单元测试。”不,货币不是浮点类型。它是一个固定精度的十进制数,用整数存储实现。它可以精确地进行比

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

现在我使用SameValue函数传递一个Epsilon参数=0.009,因为我只需要2位小数精度


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

要避免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中的浮点指令完成。结果容易出现舍入错误。哇。我真的写了这个。试图删除此项,但它作为一个示例说明了不应做的事情。;)请注意,货币类型之间的算术运算是使用浮点指令完成的。由于浮点二进制表示,结果容易出现舍入错误。