Ответ 1
Здесь мой 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.
- округление до ближайшего целого числа, дающее 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
он получит правильный ответ.