grösser / kleiner bei "unsigned" long

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
andi256
Beiträge: 100
Registriert: 06.11.2004 11:23
Computerausstattung: PB 5.30 (x64) Win7
Wohnort: Österreich

grösser / kleiner bei "unsigned" long

Beitrag von andi256 »

schön langsam finde ich es etwas "lästig" :cry: das PB keine unsigned kann.

verwende viele Sourcen von anderen Sprachen ... und die meiste Zeit geht drauf weil PB das nicht unterstützt! (echt ne tolle Fehlerquelle!)

aber das wurde ja schon zig mal durchdiskutiert .... also lassen wir das ... :cry:

So nun die Frage ...

arbeite nun mit Soucen die mit unsigned longs arbeiten ... bräuchte da tipps wie man das eleganter (& soll auch sehr schnell sein) machen (event per ASM) kann..

bräuchte
">" "<" "<=" ">=" "<>"

Code: Alles auswählen

DataSection
a:
;(Zahl = 286331152)
;$11111110
Data.b $10,$11,$11,$11
b:
;(Zahl = 4294967294)
;$FFFFFFFE
Data.b $FE,$FF,$FF,$FF
EndDataSection

Procedure grosser(a,b)
 aHigh= (a>>16)&$FFFF
 aLow = a&$FFFF
 bHigh= (b>>16)&$FFFF
 bLow = b&$FFFF
 ret=1
 If blow<alow
  ret=0
 EndIf 
 If blow>alow
  ret=1
 EndIf 
 If bhigh<ahigh
  ret=0
 EndIf 
 If bhigh>ahigh
  ret=1
 EndIf 
ProcedureReturn ret
EndProcedure

a = PeekL(?a)
b = PeekL(?b)

Debug ("Hex")
Debug ("a :"+Hex(a))
Debug ("b :"+Hex(b))
Debug ("Dez")
Debug ("a :"+Str(a))
Debug ("b :"+Str(b))

Debug" ok ... PB rechnet signed (also in meinem Fall Falsch ?) ..... "
If a < b 
 Debug ("b grösser a")
Else 
 Debug ("b nicht grösser a")
EndIf

Debug" ok ... unsigned  ..... "
If grosser(a,b) 
 Debug ("b grösser a")
EndIf
hab das für grösser gemacht .... aber das kanns doch nicht sein oder doch ?????

muss ich da für jeden fall so ne procedure starten ??

Andi256
Benutzeravatar
Froggerprogger
Badmin
Beiträge: 855
Registriert: 08.09.2004 20:02

Beitrag von Froggerprogger »

Hab grade im FASM-Handbuch mal rumgestöbert, und dort keinen ASM-Befehl für das einfache Vergleichen von zwei 32-Bit-Integer als unsigned gefunden.
Es gibt zwar CMP für das Vergleichen, aber nichts wie z.B. "CMPU" für unsigned Vergleich.
Mann könnte wohl beide Werte auf Quad-ints aufblasen, und diese dann vergleichen, aber das ist wahrscheinlich langsamer, als die untenstehenden Prozeduren. Diese brauchen nur 2 Vergleiche (und ein paar Bitoperationen zwischendrin). Das Langsamste sind also die Prozeduraufrufe an sich.
Wenn es extra schnell gehen soll, könnte man diese auch als Makro schreiben, werde ich gleich nochmal posten, dann aber ist der Aufruf etwas 'tricky'.
Hier erstmal die Prozeduren:

[edit]
Siehe weiter unten bei remi_meier und horst, da gibt es die schnelleren ASM-Lösungen, sowie noch weiter unten deren lösungen als macros
[/edit]

Code: Alles auswählen

; Some procedures to compare 2 32-bit-integers unsigned
; the scheme is always:
; - test if exactly one of them is larger than $7FFFFFFF, then return result
; - if not, then compare their lower 31 bits
; 
; = and <> work with unsigned integers, too
;
; by Froggerprogger

Procedure UINT_Greater(a.l, b.l)
  If (a ! b) & $80000000
    If a & $80000000 : ProcedureReturn 1 : Else : ProcedureReturn 0 : EndIf
  EndIf
  If a & $7FFFFFFF > b & $7FFFFFFF : ProcedureReturn 1 : Else : ProcedureReturn 0 : EndIf
EndProcedure

Procedure UINT_GreaterEq(a.l, b.l)
  If (a ! b) & $80000000
    If a & $80000000 : ProcedureReturn 1 : Else : ProcedureReturn 0 : EndIf
  EndIf
  If a & $7FFFFFFF >= b & $7FFFFFFF : ProcedureReturn 1 : Else : ProcedureReturn 0 : EndIf
EndProcedure

Procedure UINT_Less(a.l, b.l)
  If (a ! b) & $80000000
    If a & $80000000 : ProcedureReturn 0 : Else : ProcedureReturn 1 : EndIf
  EndIf
  If a & $7FFFFFFF < b & $7FFFFFFF : ProcedureReturn 1 : Else : ProcedureReturn 0 : EndIf
EndProcedure

Procedure UINT_LessEq(a.l, b.l)
  If (a ! b) & $80000000
    If a & $80000000 : ProcedureReturn 0 : Else : ProcedureReturn 1 : EndIf
  EndIf
  If a & $7FFFFFFF <= b & $7FFFFFFF : ProcedureReturn 1 : Else : ProcedureReturn 0 : EndIf
EndProcedure

;-
;- examples
;-
x.l = $80000000
y.l = $7FFFFFFF
Debug UINT_Greater(x,y)
Debug UINT_GreaterEq(x,y)
Debug UINT_Less(x,y)
Debug UINT_LessEq(x,y)
Debug ""

x.l = $80000010
y.l = $80000020
Debug UINT_Greater(x,y)
Debug UINT_GreaterEq(x,y)
Debug UINT_Less(x,y)
Debug UINT_LessEq(x,y)
Debug ""

x.l = 17
y.l = 17
Debug UINT_Greater(x,y)
Debug UINT_GreaterEq(x,y)
Debug UINT_Less(x,y)
Debug UINT_LessEq(x,y)
Debug ""
Zuletzt geändert von Froggerprogger am 13.02.2005 19:45, insgesamt 2-mal geändert.
!UD2
Benutzeravatar
remi_meier
Beiträge: 1078
Registriert: 29.08.2004 20:11
Wohnort: Schweiz

Beitrag von remi_meier »

CMP ist für unsigned und signed :wink: , der Sprungbefehl machts aus!

Code: Alles auswählen

Procedure grosser(a,b)
  MOV Eax, a
  CMP Eax, b
  JNA @f ; not above
  
  ProcedureReturn 1
  !@@:
EndProcedure
Procedure kleiner(a,b)
  MOV Eax, a
  CMP Eax, b
  JNB @f ; not below
  
  ProcedureReturn 1
  !@@:
EndProcedure
Procedure gleich(a,b)
  MOV Eax, a
  CMP Eax, b
  JNE @f ; not equal
  
  ProcedureReturn 1
  !@@:
EndProcedure
Procedure klgleich(a,b)
  MOV Eax, a
  CMP Eax, b
  JA @f ; above
  
  ProcedureReturn 1
  !@@:
EndProcedure
Procedure grgleich(a,b)
  MOV Eax, a
  CMP Eax, b
  JB @f ; below
  
  ProcedureReturn 1
  !@@:
EndProcedure
:wink:
Zuletzt geändert von remi_meier am 13.02.2005 20:38, insgesamt 1-mal geändert.
horst
Beiträge: 70
Registriert: 08.09.2004 19:33
Wohnort: München
Kontaktdaten:

Beitrag von horst »

Alternative mit RCL (rotate left thru carry, schiebt das Carry bit ins EAX register):

Code: Alles auswählen

Procedure kleiner(a,b)
  SUB Eax,Eax
  MOV Ebx,a
  CMP Ebx,b
  RCL Eax,1
ProcedureReturn 
EndProcedure 
horst
Benutzeravatar
Froggerprogger
Badmin
Beiträge: 855
Registriert: 08.09.2004 20:02

Beitrag von Froggerprogger »

...der Sprungbefehl machts aus!
Achso! Hatte mich auch schon sehr gewundert. :D

Ich hab aus Euren beiden Lösungen mal Makros gebastelt:
Makros werden direkt beim Compilieren in den Source eingebaut, wodurch man sich den zeitaufwendigen Prozeduraufruf spart. Da aber PB noch keine Makros direkt unterstützt, ist der Aufruf einer Makro etwas unschön. Weiteres dazu siehe 'Vorwort':


[edit]
Hab grad mal einige Geschwindigkeitsvergleiche gemacht: Makros sind dabei etwa 4-mal schneller. Lohnt sich also nicht wirklich für alle Zwecke, aber bei zeitkritischen schon!
[/edit]

[edit2]
Aktuelle Version siehe weiter unten
[/edit2]
Zuletzt geändert von Froggerprogger am 13.02.2005 23:47, insgesamt 2-mal geändert.
!UD2
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag von Kaeru Gaman »

gute sache, sollte man vielleicht in die tips&tricks verschieben,
das allgemeine wird so schnell gefüllt,
das sowas zu schnell in der versenkung verschwindet.
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
andi256
Beiträge: 100
Registriert: 06.11.2004 11:23
Computerausstattung: PB 5.30 (x64) Win7
Wohnort: Österreich

Beitrag von andi256 »

Danke euch herzlich ....

Code: Alles auswählen

Procedure kleiner(a,b) 
  SUB Eax,Eax 
  MOV Ebx,a 
  CMP Ebx,b 
  RCL Eax,1 
ProcedureReturn 
EndProcedure 

a= $FFFFFFFF
b= $11111111

Debug kleiner(a,b)
Debug kleiner(b,a)
Debug kleiner(a,a)
sieht ja schon sehr gut aus ...

das mit den macros :freak: .... werd ich mir event. zum code optimieren mal etwas später angucken ... bin mit asm auf kriegsfuß :mrgreen:

Danke
Andi256
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Beitrag von Helle »

Zum Code von Froggerprogger: Das Nullsetzen von EAX in den Makros muss wohl nicht sein. Das Ganze geht noch schneller wenn man die Variable res als Byte-Wert deklariert und dann die SETXX-Befehle benutzt.
Man braucht dann keine bedingten Sprünge (JA,JB usw.) mehr auszu-
führen (Sprünge sind immer Gift!). Z.B. SETGE res setzt nach einem CMP die Variable res auf 1 wenn die Bedingung erfüllt ist oder auf 0 bei Nichterfüllung; res muss so noch nichtmal vorher auf einen Wert gesetzt
werden!
Benutzeravatar
Froggerprogger
Badmin
Beiträge: 855
Registriert: 08.09.2004 20:02

Beitrag von Froggerprogger »

Danke Helle!
Das mit dem XOR eax,eax war wohl wirklich eine Copy&Paste-Leiche (und das bei 4 Zeilen Code :oops: ).
Bzgl. SetXX : Danke für den Hinweis ! Ich hatte ganz zu Anfang schonmal damit rumgespielt (als die Macro noch 20 ASM-code-Zeilen hatte /:-> ), aber dachte immer, dass die SetXX-Befehle das Register unberührt lassen, wenn die Bedingung nicht erfüllt ist, aber dem ist ja gar nicht so! Tsja "ASM-learning by doing", (leider auf Kosten derer, die den Source unbedarft übernehmen, whoops...)

Hier nun die aktuellen Versionen, welche also noch weit schlanker als zuvor sind:

(Ich denke in den Macros ist weniger als 3 Befehle wohl nicht machbar. In den Proceduren muss das XOR eax, eax erhalten bleiben, denn selbst wenn man den Rückgabetyp auf .b setzt, wird das Ergebnis durch Reste im Register verfälscht, solange man es nicht ausdrücklich mit &$FF oder einem Bytewert empfängt.)

[edit]Hier der nochmals korrigierte Code grrrgg[/edit]

Code: Alles auswählen

;/ Macros (and Procedures) for comparing two unsigned 32-bit-values (version 1.2)
;/
;/ all of them compare the first against the second parameter and
;/ store the result in the third parameter
;/
;/ To call them from PB you must introduce the line with '!', and mask
;/ all variablenames with '[v_MYVARIABLENAME]' (MYVARIABLENAME is case sensitive!!)
;/ Further you might use decimal values such as 12345,
;/ or hexadecimal values using leading '0x' instead of '$'
;/ In the macros the result-value is treated as if it were of type byte,
;/ so just the lower 8 bit are manipulated.
;/
;/ e.g. valid calls are (variables 'res', 'a', 'b' suspected to be already defined):
;/
;/ !greater 3, 9, [v_res]
;/ !smaller [v_a], 0x7FFFFFFF, [v_res]
;/ !smallerequal [v_a], [v_b], [v_res]
;/
;/ These Macros are about 4 times faster than the respective procedure.
;/ But if you don't really need this speed and want the convenience of a procedure,
;/ than you might use the ones below instead of the macros.
;/ These macros and proceudures are all independant of each other, so you can
;/ easily copy & paste the ones you need.
;/
;/ 14.02.2005 by Froggerprogger with the BIG help of remi_meier & horst & Helle

;- the macros: 
!macro greater a, b, res
!{
  !MOV  ebx,a
  !CMP  ebx,b
  !SETA byte res
!}

!macro greaterequal a, b, res
!{
  !MOV  ebx,a
  !CMP  ebx,b
  !SETAE byte res
!}

!macro smaller a, b, res
!{
  !MOV  ebx,a
  !CMP  ebx,b
  !SETB byte res
!}

!macro smallerequal a, b, res
!{
  !MOV  ebx,a
  !CMP  ebx,b
  !SETBE byte res
!}

;- the procedures:
Procedure.l UINT_Greater(a.l, b.l)
  !XOR eax,eax
  !MOV ebx,[esp]
  !CMP ebx,[esp+4]
  !SETA al
  ProcedureReturn
EndProcedure

Procedure.l UINT_GreaterEqual(a.l, b.l)
  !XOR eax,eax
  !MOV ebx,[esp]
  !CMP ebx,[esp+4]
  !SETAE al
  ProcedureReturn
EndProcedure

Procedure.l UINT_Smaller(a.l, b.l)
  !XOR eax,eax
  !MOV ebx,[esp]
  !CMP ebx,[esp+4]
  !SETB al
  ProcedureReturn
EndProcedure

Procedure.l UINT_SmallerEqual(a.l, b.l)
  !XOR eax,eax
  !MOV ebx,[esp]
  !CMP ebx,[esp+4] 
  !SETBE al
  ProcedureReturn
EndProcedure


;-
;- examples
;-

x         = $99000000
y         = $88000000
result.b  = 0           ; we might use LONG instead if we don't manipulate the upper 24 bits, though BYTE is more safe because of that

!greater [v_x], [v_y], [v_result]
Debug result
Debug UINT_Greater(x, y)

!smallerequal 0x80000000, [v_y], [v_result]
Debug result 
Debug UINT_SmallerEqual($88000000, y)

Zuletzt geändert von Froggerprogger am 14.02.2005 12:29, insgesamt 1-mal geändert.
!UD2
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Beitrag von Helle »

Hallo Froggerprogger,
mein Beispiel mit SETGE war blöd gewählt, da dieser Befehl vorzeichen-
behaftete Zahlen auswertet. In deinem Code muss natürlich SETA bzw.
SETAE verwendet werden. In den GREATER-Modulen müssen die Operanden (wieder) vertauscht werden, da vorher für RCL eigentlich auf
"kleiner" getestet wurde (eben um Carry zu setzen).
Antworten