Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
Delphi 12 Athens
|
Delphi's LargeWord-Divisionsfehler beheben
9. Sep 2005, 17:52
Leider scheint es im Delphi 'nen winzigen Fehler in den 64-Bit-Routinen zu geben.
Und zwar scheint der Compiler "immer" die Integer-Versionen (signed) zu verwenden, selbst wenn es sich um eine vorzeichenlose Variable (unsigned) handelt.
Bei der Multiplikation, Subtaktion und den Binäroperationen (and, or, shl, shr ...) ist dieses zum Glück vernachlässigbar, solange die Overlow-Checks deaktiviert sind.
Allerdings treten bei der Division (div) und Modul-Division (mod) nette Fehler auf, wenn mit Werten über 9.223.372.036.854.775.807 (>= $8000000000000000) gerechnet wird.
Im Moment bin ich mir aber noch nicht zu 100% sicher, dass dieser Fehler wirklich in allen "neueren" Delphi-Compilern enthalten ist.
Wobei D1 bis D5 auf jeden Fall Fehlerfrei ist, denn diese Versionen kennen ja noch keine Vorzeichenlosen 64-Bit Variablen, wenn sie über haupt schon mal was von 64-Bit gehört haben
Also gibt es als Erstes mal die Testmöglichkeit.
"Error - SIGNED division" sollte wohl bei den Meisten auftauchen.
Wenn jemand "OK - unsigned division" sieht, so möge er/sie sich bitte melden.
Und wer "Error - NO division result" erspäht, der sollte sich wirklich Sorgen machen -.-''
Delphi-Quellcode:
Var L: LargeWord;
L := LargeWord($8000000000000000);
L := L div 10;
If L = $0CCCCCCCCCCCCCCC Then MessageBox(0, 'OK - unsigned division', '', 0)
Else If L = LargeWord($F333333333333334) Then MessageBox(0, 'Error - SIGNED division', '', 0)
Else MessageBox(0, 'Error - NO division result', '', 0);
Lösen kann man dieses Problem dadurch, dass man sich nicht auf den Compiler verläßt und selbst die entspechenden Funktionen aufruft.
Und hier hätten wir mal die beiden Funktionen ^^
Delphi-Quellcode:
Procedure DivW64;
// unit System > __lludiv
ASM
PUSH EBP
PUSH EDI
PUSH ESI
PUSH EBX
MOV EBX, [ESP + 20]
MOV ECX, [ESP + 24]
OR ECX, ECX
JNZ @Slow
OR EDX, EDX
JZ @Quick
OR EBX, EBX
JZ @Quick
@Slow:
MOV EBP, ECX
MOV ECX, 64
XOR EDI, EDI
XOR ESI, ESI
@Loop:
SHL EAX, 1
RCL EDX, 1
RCL ESI, 1
RCL EDI, 1
CMP EDI, EBP
JB @noSub
JA @Sub
CMP ESI, EBX
JB @noSub
@Sub:
SUB ESI, EBX
SBB EDI, EBP
INC EAX
@noSub:
LOOP @Loop
JMP @Exit
@Quick:
DIV EBX
XOR EDX, EDX
@Exit:
POP EBX
POP ESI
POP EDI
POP EBP
RET 8
End;
Procedure ModW64;
// unit System > __llumod
ASM
PUSH EBP
PUSH EDI
PUSH ESI
PUSH EBX
MOV EBX, [ESP + 20]
MOV ECX, [ESP + 24]
OR ECX, ECX
jnz @Slow
OR EDX, EDX
JZ @Quick
OR EBX, EBX
JZ @Quick
@Slow:
MOV EBP, ECX
MOV ECX, 64
XOR EDI, EDI
XOR ESI, ESI
@Loop:
SHL EAX, 1
RCL EDX, 1
RCL ESI, 1
RCL EDI, 1
CMP EDI, EBP
JB @noSub
JA @Sub
CMP ESI, EBX
JB @noSub
@Sub:
SUB ESI, EBX
SBB EDI, EBP
INC EAX
@noSub:
LOOP @Loop
MOV EAX, ESI
MOV EDX, EDI
JMP @Exit
@Quick:
DIV EBX
XCHG EAX, EDX
XOR EDX, EDX
@Exit:
POP EBX
POP ESI
POP EDI
POP EBP
RET 8
End;
Aufrufen kann man diese z.B. auf folgende Weise:
Delphi-Quellcode:
Var XXX, YYY, ZZZ: LargeWord;
// ZZZ := XXX div YYY;
ASM
PUSH DWORD PTR [&YYY + 4]
PUSH DWORD PTR [&YYY]
MOV EAX, DWORD PTR [&XXX]
MOV EDX, DWORD PTR [&XXX + 4]
CALL DivW64
MOV DWORD PTR [&ZZZ], EAX
MOV DWORD PTR [&ZZZ + 4], EDX
End;
// ZZZ := XXX mod YYY;
ASM
PUSH DWORD PTR [&YYY + 4]
PUSH DWORD PTR [&YYY]
MOV EAX, DWORD PTR [&XXX]
MOV EDX, DWORD PTR [&XXX + 4]
CALL ModW64
MOV DWORD PTR [&ZZZ], EAX
MOV DWORD PTR [&ZZZ + 4], EDX
End;
Dieses wird einfach in den Pascal-Quelltext eingefügt.
Der obrige Test würde dann also so aussehen.
Delphi-Quellcode:
Var L: LargeWord;
L := LargeWord($8000000000000000);
ASM
PUSH 0
PUSH 10
MOV EAX, DWORD PTR [&L]
MOV EDX, DWORD PTR [&L + 4]
CALL DivW64
MOV DWORD PTR [&L], EAX
MOV DWORD PTR [&L + 4], EDX
End;
If L = $0CCCCCCCCCCCCCCC Then MessageBox(0, ' OK - unsigned division', ' ', 0)
Else If L = LargeWord($F333333333333334) Then MessageBox(0, ' Error - SIGNED division', ' ', 0)
Else MessageBox(0, ' Error - NO division result', ' ', 0);
PS: mit diesen Funktionen könnte man auch in den Delphiversionen bis 5 das LargeWord integrieren, dazu bräuchte man ja nur diese Funktionen auf einen LargeInt/Int64 anzuwenden.
$2B or not $2B
|