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

时间:2014-02-19 19:49:29

标签: delphi delphi-xe2 32bit-64bit

我们最近开始创建64位构建的应用程序。在比较测试期间,我们发现64位构建的计算方式不同。我有一个代码示例,演示了两个版本之间的区别。

var
  currPercent, currGross, currCalcValue : Currency;
begin

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

如果在32位版本中逐步执行此操作,则使用17.1451计算currCalcValue,而使用17.145返回64位版本。

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

2 个答案:

答案 0 :(得分:14)

这是我的SSCCE基于您的代码。请注意控制台应用程序的使用。让生活变得更加简单。

{$APPTYPE CONSOLE}

uses
  SysUtils;

var
  currPercent, currGross, 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,450,000。
  • 除以10,000,即145。
  • 乘以11,824,200给1,714,509,000。
  • 除以10,000,即171,450。呃哦,这里精度不足。
  • 将其存储在您的货币变量中。因此结果是17.145。

所以问题是64位编译器在每个中间步骤中除以10,000。大概是为了避免溢出,更可能是64位整数而不是浮点寄存器。

是这样做的计算:

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

它会得到正确的答案。

答案 1 :(得分:0)

从XE5u2开始,到目前正在编写XE6u1时已经修复了这个问题。