Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Library: Algorithmen (https://www.delphipraxis.net/28-library-algorithmen/)
-   -   Delphi Delphi's LargeWord-Divisionsfehler beheben (https://www.delphipraxis.net/53079-delphis-largeword-divisionsfehler-beheben.html)

himitsu 9. Sep 2005 16:52


Delphi's LargeWord-Divisionsfehler beheben
 
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 :zwinker:

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.


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:14 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz