Abs() für Ganzzahlen

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.
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Abs() für Ganzzahlen

Beitrag von Sicro »

Eigentlich einfach, aber vielleicht kommt nicht jeder drauf, dass man vor Variablen-Namen einfach ein Minuszeichen voranstellen kann.
Das geht übrigens auch bei Klammern "-(40 - 80)".

Code: Alles auswählen

Procedure.i AbsInt(Number.i)
  If Number < 0
    ProcedureReturn -Number
  Else
    ProcedureReturn Number
  EndIf
EndProcedure

Debug AbsInt(50)  ; gibt "50" aus
Debug AbsInt(-50) ; gibt "50" aus
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
Benutzeravatar
_sivizius
Beiträge: 98
Registriert: 23.10.2013 15:21

Re: Abs() für Ganzzahlen

Beitrag von _sivizius »

Code: Alles auswählen

intValue.q = intValue!(intValue>>63)+((intValue>>63)&1)
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Abs() für Ganzzahlen

Beitrag von NicTheQuick »

Wenn man rein auf den erzeugten ASM-Output schaut, dann ist es mit einem "If" am schnellsten. Ich habe noch eine ASM-Lösung dazu gepackt, die genauso lang ist wie die PB-Version mit dem If, da man die Sprungmarke nicht mitzählen darf.

Code: Alles auswählen

Macro BM1(var)		;Bits Minus 1
	(SizeOf(var) * 8 - 1)
EndMacro

intValue.i = -1

#VERSION = 5

CompilerSelect #VERSION
	CompilerCase 1
		intValue = intValue ! (intValue >> BM1(intValue)) + ((intValue >> BM1(intValue)) & 1)
		;   MOV    r15,qword [v_intValue]
		;   MOV    r14,qword [v_intValue]
		;   SAR    r14,63
		;   XOr    r15,r14
		;   MOV    r14,qword [v_intValue]
		;   SAR    r14,63
		;   And    r14,1
		;   ADD    r15,r14
		;   MOV    qword [v_intValue],r15
	
	CompilerCase 2
		intValue = intValue * -((((intValue >> BM1(intValue)) & 1) << 1) - 1)
		;   MOV    r15,qword [v_intValue]
		;   MOV    r14,qword [v_intValue]
		;   SAR    r14,63
		;   And    r14,1
		;   SAL    r14,1
		;   DEC    r14
		;   NEG    r14
		;   IMUL   r15,r14
		;   MOV    qword [v_intValue],r15
	
	CompilerCase 3
		intValue = intValue * (((intValue >> BM1(intValue)) & 1) * -2 + 1)
		;   MOV    r15,qword [v_intValue]
		;   MOV    r14,qword [v_intValue]
		;   SAR    r14,63
		;   And    r14,1
		;   IMUL   r14,-2
		;   INC    r14
		;   IMUL   r15,r14
		;   MOV    qword [v_intValue],r15
	
	CompilerCase 4
		If (intValue < 0)
			intValue = -intValue
		EndIf
		;   MOV    r15,qword [v_intValue]
		;   And    r15,r15
		;   JGE   _EndIf2
		;   MOV    r15,qword [v_intValue]
		;   NEG    r15
		;   MOV    qword [v_intValue],r15
		; _EndIf2:
	
	CompilerCase 5
; 		CompilerIf Not #PB_Compiler_InlineAssembly
; 			CompilerError "Bitte zuerst InlineASM in den Compiler-Optionen aktivieren."
; 		CompilerEndIf
		CompilerIf SizeOf(intValue) <> 8
			CompilerError "Der ASM-Code funktioniert nur auf einem 64-Bit-System mit einer 64-Bit-Ganzzahlvariablen."
		CompilerEndIf
		!MOV r15, qword [v_intValue]
		!MOV r14, r15
		!SAR r14, 63
		!XOR r15, r14
		!SUB r15, r14
		!MOV qword [v_intValue], r15
CompilerEndSelect

Debug intValue
Bild
Benutzeravatar
_sivizius
Beiträge: 98
Registriert: 23.10.2013 15:21

Re: Abs() für Ganzzahlen

Beitrag von _sivizius »

stimmt nicht ganz, denn AbsI() ist eine Procedure, damit wäre ein call, ein ret und und noch ein pop, etc notwendig :P
meine Lösung ist einfach kürzer als reiner Text.
GPI
Beiträge: 1511
Registriert: 29.08.2004 13:18
Kontaktdaten:

Re: Abs() für Ganzzahlen

Beitrag von GPI »

Die Procedure ist trotzdem schneller :)

Code: Alles auswählen

x=ElapsedMilliseconds()
Procedure.i AbsI(Number.i)
  If number<0
    ProcedureReturn -Number
  EndIf
  ProcedureReturn Number
EndProcedure


For i=-100000000 To 100000000
  result=AbsI(i)
Next

a$+"AbsI:"+Str(x-ElapsedMilliseconds())+Chr(10)
x=ElapsedMilliseconds()

Macro mAbsI(intValue)
  intValue!(intValue>>63)+((intValue>>63)&1)
EndMacro

For i=-100000000 To 100000000
  result=mAbsI(i)
Next
a$+"mAbsI:"+Str(x-ElapsedMilliseconds())+Chr(10)
x=ElapsedMilliseconds()


MessageRequester("Results",a$)
zumindest auf meinen PC - Debugger ausschalten nicht vergessen.

Wird in Codearchiv unter Maths\AbsI.pb auftauchen.
CodeArchiv Rebirth: Deutsches Forum Github Hilfe ist immer gern gesehen!
Nino
Beiträge: 1300
Registriert: 13.05.2010 09:26
Wohnort: Berlin

Re: Abs() für Ganzzahlen

Beitrag von Nino »

GPI hat geschrieben:Wird in Codearchiv unter Maths\AbsI.pb auftauchen.
Und wozu ist das erforderlich? :-)
Die eingebaute Abs()-Funktion kann doch auch Ganzzahlen verarbeiten, oder habe ich da etwas übersehen?
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: Abs() für Ganzzahlen

Beitrag von Sicro »

Nino hat geschrieben:Die eingebaute Abs()-Funktion kann doch auch Ganzzahlen verarbeiten, oder habe ich da etwas übersehen?
PB-Hilfe hat geschrieben:

Code: Alles auswählen

Ergebnis.f = Abs(Zahl.f)
Zahl.f
Die Zahl, von welcher der absolute Wert ermittelt werden soll. Diese Funktion arbeitet nur korrekt mit Fließkomma-Zahlen. Mit Integer (Ganzzahlen) schlägt sie fehl, wenn die Integerzahl zu groß ist (wegen Verlust der Präzision).
Veranschaulichung:

Code: Alles auswählen

 ; min. Quad:      -9223372036854775808
Define.q Value = Abs(-77777777777777777)

Debug Value ; 77777777777777776
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
Nino
Beiträge: 1300
Registriert: 13.05.2010 09:26
Wohnort: Berlin

Re: Abs() für Ganzzahlen

Beitrag von Nino »

Danke, Sicro!

Ich hatte zwar die Hilfe zu Abs() gelesen, aber nachdem ich irgendwann mal diesen Thread gelesen hatte dachte ich dass Abs() auch mit Ganzzahlen funktioniert.

Jetzt nehme ich an, dass sich die Aussage von freak in dem Thread wohl nur auf das dort angegebene Beispiel bezog, und nicht auf Abs() und Ganzzahlen im Allgemeinen.
Und die Aussage von BasicallyPure "It does work for integers but the hex value must be within the range of the variable type you are using." trifft eben so allgemein offenbar nicht zu.
Sicro hat geschrieben:

Code: Alles auswählen

 ; min. Quad:      -9223372036854775808
Define.q Value = Abs(-77777777777777777)

Debug Value ; 77777777777777776
Dieses Beispiel ist ja wirklich sehr überzeugend.
Danke nochmal!
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: Abs() für Ganzzahlen

Beitrag von Sicro »

Hallo Nino,

bitte bitte :)

Ich habe es in den von dir genannten Thread des englischen Forums nun auch mal gepostet.

Hier noch der Code für Long:

Code: Alles auswählen

Define.l Value

; min. Long:  -2147483648
Value =   Abs(-2147483648)
Debug Value ; -2147483648 => Wegen Überlauf
Debug Bin(Value, #PB_Long) ; 10000000000000000000000000000000

; min. Long: -2147483648
Value =  Abs(-2147483647)
Debug Value ; 2147483647
Debug Bin(Value, #PB_Long) ; 1111111111111111111111111111111
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Abs() für Ganzzahlen

Beitrag von NicTheQuick »

GPI hat geschrieben:Die Procedure ist trotzdem schneller :)

Code: Alles auswählen

<snip>
zumindest auf meinen PC - Debugger ausschalten nicht vergessen.
Also bei mir ist das Makro 130 ms schneller.
Bild
Antworten