ABS for .i and .q

Share your advanced PureBasic knowledge/code with the community.
User avatar
luis
Addict
Addict
Posts: 3895
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

ABS for .i and .q

Post by luis »

Since we have ABS() only for floats/doubles I tried to make one for .i and .q

Code: Select all

CompilerIf (#PB_Compiler_Processor = #PB_Processor_x86)
 val = -2147483647
 valq.q = -9223372036854775807
 CompilerElse   
 val = -9223372036854775807
 valq.q = -9223372036854775807
CompilerEndIf


Procedure.i Absi (val)
CompilerIf (#PB_Compiler_Processor = #PB_Processor_x86)
 Protected t = val >> 31 ; MSB is used to fill all the bits
 CompilerElse   
 Protected t = val >> 63 ; MSB is used to fill all the bits
CompilerEndIf

 val ! t ; v xor t
 val - t ; v sub t
 ProcedureReturn val
EndProcedure

Procedure.q Absq (val.q)
 Protected t.q = val >> 63 ; MSB is used to fill all the bits
 val ! t ; v xor t
 val - t ; v sub t
 ProcedureReturn val
EndProcedure


Debug Absi(val)
Debug Absq(valq)
"Have you tried turning it off and on again ?"
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ABS for .i and .q

Post by wilbert »

On my computer simply doing

Code: Select all

Procedure.q Absq (val.q)
  If val < 0
    ProcedureReturn -val
  Else
    ProcedureReturn val
  EndIf
EndProcedure
is much faster.

Asm solutions

Code: Select all

Procedure.l lAbs(Value.l)
  !mov eax, [p.v_Value]
  !cdq
  !add eax, edx
  !xor eax, edx
  ProcedureReturn
EndProcedure

Procedure.q qAbs(Value.q)
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    !mov rax, [p.v_Value]
    !cqo
    !add rax, rdx
    !xor rax, rdx
  CompilerElse
    !mov eax, [p.v_Value]
    !mov edx, [p.v_Value + 4]
    !mov ecx, edx
    !sar ecx, 31
    !add eax, ecx
    !adc edx, ecx
    !xor eax, ecx
    !xor edx, ecx
  CompilerEndIf  
  ProcedureReturn
EndProcedure

Procedure.i iAbs(Value.i)
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    !mov rax, [p.v_Value]
    !cqo
    !add rax, rdx
    !xor rax, rdx
  CompilerElse
    !mov eax, [p.v_Value]
    !cdq
    !add eax, edx
    !xor eax, edx
  CompilerEndIf  
  ProcedureReturn
EndProcedure
Last edited by wilbert on Mon Dec 23, 2013 9:12 am, edited 3 times in total.
User avatar
Michael Vogel
Addict
Addict
Posts: 2807
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: ABS for .i and .q

Post by Michael Vogel »

Cool,
I would like to have iAbs, iMax, iMin, iSignum, iScale, iLimit etc. (much more than iPad & Co) :wink:

Next vision: all together will be collected into a PureBasic.pbi (which could be included automatically to each project by default)
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ABS for .i and .q

Post by wilbert »

Michael Vogel wrote:I would like to have iAbs, iMax, iMin, iSignum, iScale, iLimit etc.
Min and Max

Code: Select all

Procedure.i iMin (val1.i, val2.i)
  EnableASM
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    MOV eax, val1
    CMP eax, val2
    CMOVG eax, val2
  CompilerElse
    MOV rax, val1
    CMP rax, val2
    CMOVG rax, val2
  CompilerEndIf
  DisableASM
  ProcedureReturn
EndProcedure

Procedure.i iMax (val1.i, val2.i)
  EnableASM
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    MOV eax, val1
    CMP eax, val2
    CMOVNG eax, val2
  CompilerElse
    MOV rax, val1
    CMP rax, val2
    CMOVNG rax, val2
  CompilerEndIf
  DisableASM
  ProcedureReturn
EndProcedure

Procedure.i iAbs (val.i)
  EnableASM
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    MOV eax, val
    NEG eax
    CMOVS eax, val
  CompilerElse
    MOV rax, val
    NEG rax
    CMOVS rax, val
  CompilerEndIf
  DisableASM
  ProcedureReturn
EndProcedure
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Re: ABS for .i and .q

Post by Psychophanta »

Code: Select all

Procedure.q AbsQ(val.d)
  ProcedureReturn Abs(val)
EndProcedure
Procedure.i AbsI(val.d)
  ProcedureReturn Abs(val)
EndProcedure
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
User avatar
Michael Vogel
Addict
Addict
Posts: 2807
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: ABS for .i and .q

Post by Michael Vogel »

wilbert wrote: Min and Max
Thanks, Wilbert.

( 1 ) your abs code will be used in my programs now...

Code: Select all

Procedure.i AbsInt(x.i)
	
	#AllowAssembler=%1111; all assembler codes active

	CompilerIf #AllowAssembler&%0010

		CompilerIf #PB_Compiler_Processor=#PB_Processor_x86
			MOV eax,n
			NEG eax
			CMOVS eax,n
		CompilerElse
			MOV rax,n
			NEG rag
			CMOVS rax,n
		CompilerEndIf
		ProcedureReturn
	
	CompilerElse

		If x<0
			x!-1
			x+1
		EndIf
		ProcedureReturn x
	
	CompilerEndIf

EndProcedure
( 2 ) I also tried to write a macro solution, just for fun (not because a procedure is sooo much slower than a macro :wink:)

Code: Select all

Macro AbsInt(n)
	(n*(n>>32)|1)
EndMacro

Debug AbsInt(9)
Debug AbsInt(-9)
Debug AbsInt(AbsInt(-9))
( 3 ) an additional routine, could be useful...

Code: Select all

Procedure.l Check(x.l,min.l,max.l)

	CompilerIf #AllowAssembler&%0010
		MOV eax,x			
		CMP eax,min		
		JNG Chklow		
		CMP eax,max		
		JNL Chkhigh		
		JMP Chkterm		
		!Chklow:
		MOV eax,min		
		JMP Chkterm		
		!Chkhigh:
		MOV eax,max		
		!Chkterm:
		ProcedureReturn

	CompilerElse
		If x<min
			ProcedureReturn min
		ElseIf x>max
			ProcedureReturn max
		Else
			ProcedureReturn x
		EndIf

	CompilerEndIf

EndProcedure
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ABS for .i and .q

Post by wilbert »

@Michael Vogel, how does your #AllowAssembler work ?
Michael Vogel wrote:an additional routine, could be useful...
Similar to my previous code it could be written like this ...

Code: Select all

Procedure.i iLimit(val.i, min.i, max.i)
  EnableASM
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    MOV eax, val
    CMP eax, min
    CMOVNG eax, min
    CMP eax, max
    CMOVG eax, max
  CompilerElse
    MOV rax, val
    CMP rax, min
    CMOVNG rax, min
    CMP rax, max
    CMOVG rax, max
  CompilerEndIf
  DisableASM
  ProcedureReturn
EndProcedure
User avatar
Michael Vogel
Addict
Addict
Posts: 2807
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: ABS for .i and .q

Post by Michael Vogel »

wilbert wrote:@Michael Vogel, how does your #AllowAssembler work ?
In larger projects, I try to check different optimization methods by using alternative routines for integer handling, string functions, graphics etc. -- each procedure gets a binary code (%001, %010, %100 etc.) according to it's "group".

So I can do easy tests by just changing the #AllowAssembler constant by setting the appropriate group flags.

PS: thanks for the assembler code, I knew you will make a better version (will check, what could be next) :wink:
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ABS for .i and .q

Post by wilbert »

Here's also a iSign function

Code: Select all

Procedure.i iMin (val1.i, val2.i)
  EnableASM
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    MOV eax, val1
    CMP eax, val2
    CMOVG eax, val2
  CompilerElse
    MOV rax, val1
    CMP rax, val2
    CMOVG rax, val2
  CompilerEndIf
  DisableASM
  ProcedureReturn
EndProcedure

Procedure.i iMax (val1.i, val2.i)
  EnableASM
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    MOV eax, val1
    CMP eax, val2
    CMOVNG eax, val2
  CompilerElse
    MOV rax, val1
    CMP rax, val2
    CMOVNG rax, val2
  CompilerEndIf
  DisableASM
  ProcedureReturn
EndProcedure

Procedure.i iAbs (val.i)
  EnableASM
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    MOV eax, val
    NEG eax
    CMOVS eax, val
  CompilerElse
    MOV rax, val
    NEG rax
    CMOVS rax, val
  CompilerEndIf
  DisableASM
  ProcedureReturn
EndProcedure

Procedure.i iLimit(val.i, min.i, max.i)
  EnableASM
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    MOV eax, val
    CMP eax, min
    CMOVNG eax, min
    CMP eax, max
    CMOVG eax, max
  CompilerElse
    MOV rax, val
    CMP rax, min
    CMOVNG rax, min
    CMP rax, max
    CMOVG rax, max
  CompilerEndIf
  DisableASM
  ProcedureReturn
EndProcedure

Procedure.i iSign (val.i)
  EnableASM
  CMP val, 0
  !setg al
  !setl ah
  !sub al, ah
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    !movsx eax, al
  CompilerElse
    !movsx rax, al
  CompilerEndIf
  DisableASM
  ProcedureReturn
EndProcedure

Procedure.i iColorLimit(val.i)
  EnableASM
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    MOV eax, val
    !test eax, -256
    !cmovg eax, [cLimit255]
    !cmovs eax, [cLimit0]
  CompilerElse
    MOV rax, val
    !test rax, -256
    !cmovg rax, [cLimit255]
    !cmovs rax, [cLimit0]
  CompilerEndIf
  DisableASM
  ProcedureReturn
  !cLimit255:
  !dd 255
  !cLimit0:
  !dd 0, 0
EndProcedure
Last edited by wilbert on Fri Jan 27, 2012 8:01 pm, edited 2 times in total.
User avatar
Michael Vogel
Addict
Addict
Posts: 2807
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: ABS for .i and .q

Post by Michael Vogel »

Hopefully Wilbert won't stop soon writing such fine functions :wink:

Have some ideas for new things (I still have problems to modify your nice examples to make other rotines myself)...

[ 1 ] (often used, but not optimized) procedure for iLimit(x,0,255):

Code: Select all

Procedure.i ColorLimit(x.i)

	CompilerIf #AllowAssembler&%010
		!mov eax,dword[p.v_x]
		!xor edx,edx
		!cmp eax,0
		!cmovl eax,edx
		!mov edx,255
		!cmp eax,edx
		!cmovg eax,edx
		ProcedureReturn

	CompilerElse

		If x<0
			ProcedureReturn 0
		ElseIf x>255
			ProcedureReturn 255
		Else
			ProcedureReturn x
		EndIf

	CompilerEndIf

EndProcedure
[ 2 ] the following example uses a relatively complex function "Scale", which could be useful in many situations (antialiasing, color blending etc.):

Code: Select all

Procedure.l Scale(A,B,n,total)
	ProcedureReturn ((A*n+B*(total-n))/total)
EndProcedure
Procedure.l ColorScale(ColA,ColB,n,total)
	ProcedureReturn RGB(Scale(Red(ColA),Red(ColB),n,total),Scale(Green(ColA),Green(ColB),n,total),Scale(Blue(ColA),Blue(ColB),n,total))
EndProcedure

Debug Hex(ColorScale(#Red,#Blue,1,4)); calculate a color between #Red and #Green, in a 1/4 distance from #Red
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ABS for .i and .q

Post by wilbert »

I updated my iSign procedure above since it contained a small bug.
I also added an alternative approach to your ColorLimit procedure. Yours works also fine of course :)

As for your Scale and ColorScale, is there any reason not to use a float to combine the last two arguments like Scale(A, B, perc.f) ?
For example Scale(1, 10, 0.75)
User avatar
Michael Vogel
Addict
Addict
Posts: 2807
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: ABS for .i and .q

Post by Michael Vogel »

wilbert wrote:As for your Scale and ColorScale, is there any reason not to use a float to combine the last two arguments like Scale(A, B, perc.f) ?
For example Scale(1, 10, 0.75)
Not sure, I thought, using floating point would be slower...
...I remember, that an assembler opcode did multiplicate and divide integer values within few clock rates -- bad luck, it is an assembler function of the 68000-cpu...
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ABS for .i and .q

Post by wilbert »

Complicated that ColorScale.
You can try this

Code: Select all

Procedure.l ColorScale(Col1.l, Col2.l, perc.f)
  EnableASM
  MOV eax, Col1
  !movd xmm0, eax
  MOV eax, Col2
  !movd xmm1, eax
  MOV eax, perc
  !movd xmm3, eax
  !mulss xmm3, [csFloat256]
  !cvtps2dq xmm3, xmm3
  !pshuflw xmm3, xmm3, 0
  !movq xmm2, [csWord256x4]
  !psubw xmm2, xmm3
  !pxor xmm4, xmm4
  !punpcklbw xmm0, xmm4
  !punpcklbw xmm1, xmm4
  !pmullw xmm0, xmm2
  !pmullw xmm1, xmm3
  !paddw xmm0, xmm1
  !psrlw xmm0, 8
  !packuswb xmm0, xmm0
  !movd eax, xmm0
  DisableASM
  ProcedureReturn
  !csWord256x4:
  !dw 0x0100, 0x0100, 0x0100, 0x0100
  !csFloat256:
  !dd 0x43800000 
EndProcedure
Without sse, just plain ASM (slower on my computer)

Code: Select all

Procedure.l ColorScale(Col1.l, Col2.l, n.l, total.l)
  EnableASM
  MOV ecx, Col1
  CMP total, 0
  JZ l_cs_exit
  
  MOV ecx, Col2
  MOVZX eax, cl
  MUL n
  DIV total
  MOV cl, al
  MOVZX eax, ch
  MUL n
  DIV total
  MOV ch, al
  ROL ecx, 16
  MOVZX eax, cl
  MUL n
  DIV total
  MOV cl, al
  MOVZX eax, ch
  MUL n
  DIV total
  MOV ch, al
  ROL ecx, 16
  MOV Col2, ecx
  
  MOV ecx, total
  SUB ecx, n
  MOV n, ecx
  
  MOV ecx, Col1
  MOVZX eax, cl
  MUL n
  DIV total
  MOV cl, al
  MOVZX eax, ch
  MUL n
  DIV total
  MOV ch, al
  ROL ecx, 16
  MOVZX eax, cl
  MUL n
  DIV total
  MOV cl, al
  MOVZX eax, ch
  MUL n
  DIV total
  MOV ch, al
  ROL ecx, 16
  ADD ecx, Col2

  cs_exit:
  MOV eax, ecx
  DisableASM
  ProcedureReturn
EndProcedure
User avatar
Michael Vogel
Addict
Addict
Posts: 2807
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: ABS for .i and .q

Post by Michael Vogel »

wilbert wrote:Complicated that ColorScale.
You can try this
...
Without sse, just plain ASM (slower on my computer)[/code]
The second code takes also (10 times) longer than the first code here...

Even the simplified code (for simple integers, not colors) is not that fast...

Code: Select all

Procedure.l Scale(Col1.l, Col2.l, n.l, total.l)
	EnableASM
	CMP total, 0
	JZ l_scale_exit
	MOV eax, Col2
	SUB eax, Col1
	MUL n
	DIV total
	ADD eax,Col1
	scale_exit:
	DisableASM
	ProcedureReturn
EndProcedure

Debug scale(200,240,1,4)
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ABS for .i and .q

Post by wilbert »

Michael Vogel wrote:Even the simplified code (for simple integers, not colors) is not that fast...
That surprised me.

This should be faster (unfortunately not OS X compatible due to a PB bug)

Code: Select all

Procedure.i iScale(val1.i, val2.i, perc.f)
  EnableASM
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    !cvtsi2sd xmm0, [p.v_val1]
    !cvtsi2sd xmm1, [p.v_val2]
    !cvtss2sd xmm2, [p.v_perc]
    !movq xmm3, xmm0
    !mulsd xmm0, xmm2
    !mulsd xmm1, xmm2
    !subsd xmm3, xmm0
    !addsd xmm1, xmm3
    !cvtsd2si eax, xmm1
  CompilerElse
    MOV rax, val1
    !cvtsi2sd xmm0, rax
    MOV rax, val2
    !cvtsi2sd xmm1, rax
    !cvtss2sd xmm2, [p.v_perc]
    !movq xmm3, xmm0
    !mulsd xmm0, xmm2
    !mulsd xmm1, xmm2
    !subsd xmm3, xmm0
    !addsd xmm1, xmm3
    !cvtsd2si rax, xmm1
  CompilerEndIf
  DisableASM
  ProcedureReturn
EndProcedure
Post Reply