【问题标题】:How does Delphi round Currency during multiplicationDelphi如何在乘法过程中四舍五入货币
【发布时间】:2016-09-30 19:48:06
【问题描述】:
rate : Currency;
mod1 : Currency;
mod2 : Currency;
mod_rate : Currency;

rate := 29.90;
mod1 := 0.95;
mod2 := 1.39;

mod_rate := rate * mod1 * mod2;

如果您在计算器上执行此计算,您会得到39.45295 的值。由于 Delphi 的 Currency 数据类型仅精确到小数点后 4 位,因此它在内部对值进行四舍五入。我的测试表明它使用了银行家的四舍五入,因此 mod_rate 应该包含 39.4530 的值,但是在这种特殊情况下它会截断为 39.4529

我有 40,000 个这样的计算,除了上面的以外,所有的都是正确的。这是一个四舍五入的例子:

rate := 32.25;
mod1 := 0.90;
mod2 := 1.15;

这等于在计算器上的33.37875,根据银行家的四舍五入将转到33.3788,这就是德尔福所做的。

有人能解释一下 Delphi 在这里做什么吗?

【问题讨论】:

  • 我的猜测是 39.4595 不能完全用浮点表示。舍入没有错,但可表示性。使用十进制算术而不是货币。
  • 货币是定点数据类型。除非将两种货币相乘产生一个浮点数。
  • @Chet:Currency 值在内部存储为 64 位整数,这些整数乘以 10000 以去除小数,但对 Currency 值执行的数学运算对这些整数使用浮点数学指令。
  • 您可以在此处查看 39.45295 的浮点表示:What is the exact value of a floating-point variable?。扩展精度值低于 39.45295,因此向下舍入。
  • 看看Decimals,这是一个以更高的精度处理浮点值的库,尽管是以性能为代价的。

标签: delphi rounding


【解决方案1】:

Currency 计算发生在具有扩展精度的 fpu 中,即使货币本身是 64 位整数值。

您遇到的问题是浮点二进制表示,而不是舍入错误。 你可以在这里看到 39.45295 的浮点表示:What is the exact value of a floating-point variable?

39.45295 = + 39.45294 99999 99999 99845 67899 95771 03240 88111 51981 35375 97656 25

扩展精度值低于 39.45295,因此向下舍入。

使用十进制算术库来避免此类错误。


要查看浮点运算是在 fpu 中进行的 currency 计算,这里是一个反汇编:

mod_rate := rate * mod1 * mod2;
0041D566 DF2D20584200     fild qword ptr [$00425820]
0041D56C DF2D28584200     fild qword ptr [$00425828]
0041D572 DEC9             fmulp st(1)
0041D574 DF2D30584200     fild qword ptr [$00425830]
0041D57A DEC9             fmulp st(1)
0041D57C D835C4D54100     fdiv dword ptr [$0041d5c4]
0041D582 DF3D38584200     fistp qword ptr [$00425838]
0041D588 9B               wait 

fmulpfdiv 以扩展精度完成。 如果使用整数运算,则指令将是 fimulfidiv

【讨论】:

  • 我已经准备好接受这个答案了。您能否引用一些说明 Delphi 将货币处理为浮点数的文档?
  • 是的,我正准备分析程序集进行验证,谢谢发布。
  • 801 / 2(两种货币)结果为 400.5。根据这个(pages.cs.wisc.edu/~rkennedy/exact-float?number=400.5),它可以用浮点数精确表示。然而,四舍五入到 401。我敢肯定这仍然是一个 FP 问题,但它很烦人。
  • mod1 := 801.0; mod2 := 2.0; mod_rate := mod1/mod2; 在我的计算机上使用货币变量导致 400.5。 Round(mod_rate) 结果为 400.0。
  • 我更彻底地查看了代码,801 是一个整数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-10
  • 2020-04-08
  • 2010-11-13
  • 1970-01-01
  • 1970-01-01
  • 2018-02-14
  • 2018-01-07
相关资源
最近更新 更多