Page 1 of 2
ABS for .i and .q
Posted: Sat Dec 10, 2011 2:01 am
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)
Re: ABS for .i and .q
Posted: Sat Dec 10, 2011 7:37 am
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
Re: ABS for .i and .q
Posted: Sat Dec 10, 2011 9:18 am
by Michael Vogel
Cool,
I would like to have
iAbs,
iMax,
iMin,
iSignum,
iScale,
iLimit etc. (much more than iPad & Co)
Next vision: all together will be collected into a
PureBasic.pbi (which could be included automatically to each project by default)
Re: ABS for .i and .q
Posted: Sat Dec 10, 2011 9:50 am
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
Re: ABS for .i and .q
Posted: Fri Dec 16, 2011 2:45 pm
by Psychophanta
Code: Select all
Procedure.q AbsQ(val.d)
ProcedureReturn Abs(val)
EndProcedure
Procedure.i AbsI(val.d)
ProcedureReturn Abs(val)
EndProcedure
Re: ABS for .i and .q
Posted: Wed Dec 28, 2011 3:56 pm
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

)
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
Re: ABS for .i and .q
Posted: Wed Dec 28, 2011 4:17 pm
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
Re: ABS for .i and .q
Posted: Fri Dec 30, 2011 10:14 am
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)

Re: ABS for .i and .q
Posted: Fri Dec 30, 2011 6:15 pm
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
Re: ABS for .i and .q
Posted: Fri Dec 30, 2011 10:38 pm
by Michael Vogel
Hopefully Wilbert won't stop soon writing such fine functions
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
Re: ABS for .i and .q
Posted: Sat Dec 31, 2011 7:52 am
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)
Re: ABS for .i and .q
Posted: Sat Dec 31, 2011 8:30 am
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...
Re: ABS for .i and .q
Posted: Sat Dec 31, 2011 11:52 am
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
Re: ABS for .i and .q
Posted: Mon Jan 02, 2012 3:50 pm
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)
Re: ABS for .i and .q
Posted: Mon Jan 02, 2012 4:57 pm
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