Delphi 货币乘法时如何舍入

How does Delphi round Currency during multiplication

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 位小数,因此它在内部对值进行四舍五入。我的测试表明它使用 Banker 的舍入,因此 mod_rate 应该包含 39.4530 的值,但是在这种特殊情况下它被截断为 39.4529.

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

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

这在计算器上等于 33.37875,根据 Banker 的四舍五入会转到 33.3788,这就是 Delphi 所做的。

有人可以阐明 Delphi 在这里做什么吗?

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,因此向下舍入。

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


为了currency计算,看到浮点运算是在fpu中进行的,这里是反汇编:

mod_rate := rate * mod1 * mod2;
0041D566 DF2D20584200     fild qword ptr [[=10=]425820]
0041D56C DF2D28584200     fild qword ptr [[=10=]425828]
0041D572 DEC9             fmulp st(1)
0041D574 DF2D30584200     fild qword ptr [[=10=]425830]
0041D57A DEC9             fmulp st(1)
0041D57C D835C4D54100     fdiv dword ptr [[=10=]41d5c4]
0041D582 DF3D38584200     fistp qword ptr [[=10=]425838]
0041D588 9B               wait 

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