Seite 1 von 2

Abs() für Ganzzahlen

Verfasst: 16.03.2014 10:50
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

Re: Abs() für Ganzzahlen

Verfasst: 19.03.2014 22:06
von _sivizius

Code: Alles auswählen

intValue.q = intValue!(intValue>>63)+((intValue>>63)&1)

Re: Abs() für Ganzzahlen

Verfasst: 19.03.2014 23:14
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

Re: Abs() für Ganzzahlen

Verfasst: 20.03.2014 18:18
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.

Re: Abs() für Ganzzahlen

Verfasst: 17.04.2016 15:24
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.

Re: Abs() für Ganzzahlen

Verfasst: 17.04.2016 19:00
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?

Re: Abs() für Ganzzahlen

Verfasst: 17.04.2016 19:41
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

Re: Abs() für Ganzzahlen

Verfasst: 17.04.2016 20:31
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!

Re: Abs() für Ganzzahlen

Verfasst: 17.04.2016 22:30
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

Re: Abs() für Ganzzahlen

Verfasst: 18.04.2016 10:30
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.