问题描述
我们最近开始了为我们的应用程序创建 64 位版本的过程.在比较测试期间,我们发现 64 位构建的计算方式不同.我有一个代码示例来演示两个构建之间的区别.
varcurrPercent, currGross, currCalcValue : 货币;开始currGross := 1182.42;currPercent := 1.45;currCalcValue := (currGross * (currPercent * StrToCurr('.01')));ShowMessage(CurrToStr(currCalcValue));结尾;
如果您在 32 位版本中逐步执行此操作,则 currCalcValue 的计算值为 17.1451,而 64 位版本的计算值为 17.145.
为什么 64 位版本不计算额外的小数位?所有变量都定义为 4 个十进制货币值.
这是我基于您的代码的 SSCCE.请注意控制台应用程序的使用.让生活更简单.
{$APPTYPE 控制台}用途系统实用程序;无功currPercent, currGross, currCalcValue : 货币;开始currGross := 1182.42;currPercent := 1.45;currCalcValue := (currGross * (currPercent * StrToCurr('.01')));Writeln(CurrToStr(currCalcValue));读入;结尾.
现在查看生成的代码.前 32 位:
Project3.dpr.13: currCalcValue := (currGross * (currPercent * StrToCurr('.01')));0041C409 8D45EC lea eax,[ebp-$14]0041C40C BADCC44100 mov edx,$0041c4dc0041C411 E8A6A2FEFF 调用@USrLAsg0041C416 8B1504E74100 mov edx,[$0041e704]0041C41C 8B45EC mov eax,[ebp-$14]0041C41F E870AFFFFF 调用 StrToCurr0041C424 DF7DE0 fistp qword ptr [ebp-$20]0041C427 9B 等待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 等待和 64 位:
Project3.dpr.13: currCalcValue := (currGross * (currPercent * StrToCurr('.01')));0000000000428A0E 488D4D38 lea rcx,[rbp+$38]0000000000428A12 488D1513010000 铅 rdx,[rel $00000113]0000000000428A19 E84213FEFF 调用@USrLAsg0000000000428A1E 488B4D38 mov rcx,[rbp+$38]0000000000428A22 488B155F480000 mov rdx,[rel $0000485f]0000000000428A29 E83280FFFF 调用 StrToCurr0000000000428A2E 4889C1 mov rcx,rax0000000000428A31 488B0510E80000 mov rax,[rel $0000e810]0000000000428A38 48F7E9 imul rcx0000000000428A3B C7C110270000 mov ecx,$000027100000000000428A41 48F7F9 idiv rcx0000000000428A44 488BC8 mov rcx,rax0000000000428A47 488B0502E80000 mov rax,[rel $0000e802]0000000000428A4E 48F7E9 imul rcx0000000000428A51 C7C110270000 mov ecx,$000027100000000000428A57 48F7F9 idiv rcx0000000000428A5A 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
它会得到正确的答案.
We recently started the process of creating 64 bit builds of our applications. During comparison testing we found that the 64 bit build is calculating differently. I have a code sample that demonstrates the difference between the two builds.
var
currPercent, currGross, currCalcValue : Currency;
begin
currGross := 1182.42;
currPercent := 1.45;
currCalcValue := (currGross * (currPercent * StrToCurr('.01')));
ShowMessage(CurrToStr(currCalcValue));
end;
If you step through this in the 32 bit version, currCalcValue is calculated with 17.1451 while the 64 bit version comes back with 17.145.
Why isn't the 64 bit build calculating out the extra decimal place? All variables are defined as 4 decimal currency values.
Here's my SSCCE based on your code. Note the use of a console application. Makes life much simpler.
{$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.
Now look at the code that is generated. First 32 bit:
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
And the 64 bit:
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
Note that the 32 bit code performs the arithmetic on the FPU, but the 64 bit code performs it using integer arithmetic. That's the key difference.
In the 32 bit code, the following calculation is performed:
- Convert '0.01' to currency, which is 100, allowing for the fixed point shift of 10,000.
- Load 14,500 into the FPU.
- Multiply by 100 giving 1,450,000.
- Multiply by 11,824,200 giving 17,145,090,000,000.
- Divide by 10,000^2 giving 171,450.9.
- Round to the nearest integer giving 171,451.
- Store that in your currency variable. Hence the result is 17.1451.
Now, in the 64 bit code, it's a little different. Because we use 64 bit integers all the way. It looks like this:
- Convert '0.01' to currency, which is 100.
- Multiply by 14,500 which is 1,450,000.
- Divide by 10,000 which is 145.
- Multiply by 11,824,200 giving 1,714,509,000.
- Divide by 10,000 which is 171,450. Uh-oh, loss of precision here.
- Store that in your currency variable. Hence the result is 17.145.
So the issue is that the 64 bit compiler divides by 10,000 at each intermediate step. Presumably to avoid overflow, much more likely in a 64 bit integer than a floating point register.
Were it to do the calculation like this:
100 * 14,500 * 11,824,200 / 10,000 / 10,000
it would get the right answer.
这篇关于为什么 64 位 Delphi 应用程序计算的结果与 32 位构建不同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!