为什么64位Delphi应用程序计算不同于32位构建的结果?

我们最近开始创建64位构建我们的应用程序的过程.在比较测试中,我们发现64位版本的计算方式不同.我有一个代码示例来演示两个构建之间的区别.
var
  currPercent,currGross,currCalcValue : Currency;
begin

  currGross := 1182.42;
  currPercent := 1.45;
  currCalcValue := (currGross * (currPercent * StrToCurr('.01')));
  ShowMessage(CurrToStr(currCalcValue));
end;

如果您在32位版本中执行此操作,则currCalcValue将使用17.1451计算,而64位版本则以17.145返回.

为什么64位版本不计算出额外的小数位?所有变量都定义为4个十进制货币值.

解决方法

这是我的SSCCE基于你的代码.请注意使用控制台应用程序.使生活更简单.
{$APPTYPE CONSOLE}

uses
  SysUtils;

var
  currPercent,currCalcValue : Currency;
begin
  currGross := 1182.42;
  currPercent := 1.45;
  currCalcValue := (currGross * (currPercent * StrToCurr('.01')));
  Writeln(CurrToStr(currCalcValue));
  Readln;
end.

现在看看生成的代码.第32位:

Project3.dpr.13: currCalcValue := (currGross * (currPercent * StrToCurr('.01')));
0041C409 8D45EC           lea eax,[ebp-$14]
0041C40C BADCC44100       mov edx,$0041c4dc
0041C411 E8A6A2FEFF       call @UStrLAsg
0041C416 8B1504E74100     mov edx,[$0041e704]
0041C41C 8B45EC           mov eax,[ebp-$14]
0041C41F E870AFFFFF       call StrToCurr
0041C424 DF7DE0           fistp qword ptr [ebp-$20]
0041C427 9B               wait 
0041C428 DF2DD83E4200     fild qword ptr [$00423ed8]
0041C42E DF6DE0           fild qword ptr [ebp-$20]
0041C431 DEC9             fmulp st(1)
0041C433 DF2DE03E4200     fild qword ptr [$00423ee0]
0041C439 DEC9             fmulp st(1)
0041C43B D835E4C44100     fdiv dword ptr [$0041c4e4]
0041C441 DF3DE83E4200     fistp qword ptr [$00423ee8]
0041C447 9B               wait 

而64位:

Project3.dpr.13: currCalcValue := (currGross * (currPercent * StrToCurr('.01')));
0000000000428A0E 488D4D38         lea rcx,[rbp+$38]
0000000000428A12 488D1513010000   lea rdx,[rel $00000113]
0000000000428A19 E84213FEFF       call @UStrLAsg
0000000000428A1E 488B4D38         mov rcx,[rbp+$38]
0000000000428A22 488B155F480000   mov rdx,[rel $0000485f]
0000000000428A29 E83280FFFF       call StrToCurr
0000000000428A2E 4889C1           mov rcx,rax
0000000000428A31 488B0510E80000   mov rax,[rel $0000e810]
0000000000428A38 48F7E9           imul rcx
0000000000428A3B C7C110270000     mov ecx,$00002710
0000000000428A41 48F7F9           idiv rcx
0000000000428A44 488BC8           mov rcx,rax
0000000000428A47 488B0502E80000   mov rax,[rel $0000e802]
0000000000428A4E 48F7E9           imul rcx
0000000000428A51 C7C110270000     mov ecx,$00002710
0000000000428A57 48F7F9           idiv rcx
0000000000428A5A 488905F7E70000   mov [rel $0000e7f7],rax

请注意,32位代码对FPU执行算术,但64位代码使用整数算术执行.这是关键的区别.

在32位代码中,执行以下计算:

>将“0.01”兑换为货币,即100,允许固定点移动10,000.
>将14,500加载到FPU中.
>乘以100给1,450,000.
>增加11,824,200元,为17,145,090,000,000元.
>除以10,000 ^ 2给予171,450.9.
>舍入到最接近的整数,给出171,451.
>将其存储在您的货币变量中.结果是17.1451.

现在,在64位代码中,它有点不同.因为我们一直使用64位整数.看起来像这样:

>将“0.01”兑换为货币,即100.
>乘以14,500,即1,000.
>除以10,这是145.
>乘以11,200给1,714,509,这是171,450.呃哦,在这里丢了精度.
>将其存储在您的货币变量中.结果是17.145.

所以问题是64位编译器在每一个中间步骤中都会划分10,000.大概是为了避免溢出,比浮点寄存器更有可能在64位整数.

如何做这样的计算:

100 * 14,500 * 11,200 / 10,000 / 10,000

它会得到正确的答案.

相关文章

 从网上看到《Delphi API HOOK完全说明》这篇文章,基本上都...
  从网上看到《Delphi API HOOK完全说明》这篇文章,基本上...
ffmpeg 是一套强大的开源的多媒体库 一般都是用 c/c+&#x...
32位CPU所含有的寄存器有:4个数据寄存器(EAX、EBX、ECX和ED...
1 mov dst, src dst是目的操作数,src是源操作数,指令实现的...