Page 2 of 3
Re: Max() and Min(), procedure and macro variants.
Posted: Fri Feb 22, 2013 5:34 pm
by skywalk
With v5.10, add Bool() around expressions.
Code: Select all
Macro Min(a,b)
Bool(((Not a<b)*b)|((Not b<a)*a))
EndMacro
Re: Max() and Min(), procedure and macro variants.
Posted: Fri Feb 22, 2013 5:39 pm
by Shield
So the minimum value is always 0 or 1?

Re: Max() and Min(), procedure and macro variants.
Posted: Fri Feb 22, 2013 5:51 pm
by Demivec
Working code for v5.10
Code: Select all
Macro Min(a,b)
(Bool((a) <= (b)) * (a) + Bool((b) < (a)) * (b))
EndMacro
Macro Max(a,b)
(Bool((a) >= (b)) * (a) + Bool((b) > (a)) * (b))
EndMacro
Re: Max() and Min(), procedure and macro variants.
Posted: Fri Feb 22, 2013 5:56 pm
by skywalk
Sorry, I didn't run the code

Thanks Demivec

Re: Max() and Min(), procedure and macro variants.
Posted: Fri Feb 22, 2013 7:29 pm
by WilliamL
Thanks skywalk, Demivec
That was my way of asking how Bool would work...
[later]
Thanks Rescator! That saves me some typing. I'm using ElapsedMilliseconds(), for timing, on the Mac.
Max10/Min10 396ms
Max20/Min20 837ms
Max30/Min30 500ms
Max40/Min40 1261ms
If Long: 268ms
if single: 246ms
If double: 231ms
Max50/Min50 long: 389ms
Max50/Min50 single: 641ms
Max50/Min50 double: 631ms
Re: Max() and Min(), procedure and macro variants.
Posted: Fri Feb 22, 2013 9:18 pm
by Rescator
Updated
Re: Max() and Min(), procedure and macro variants.
Posted: Wed Jun 28, 2023 2:36 pm
by Michael Vogel
Was using the following macros for a while, but there are some miraculous effects when using Val() in combination with the Purebasic version 5.73x86...
Code: Select all
Macro Min(a,b)
(Bool((a)<=(b))*(a)+Bool((b)<(a))*(b))
EndMacro
Macro Max(a,b)
(Bool((a)>=(b))*(a)+Bool((b)>(a))*(b))
EndMacro
value.s="111"
OpenWindow(0,10,10,70,50,"Hmm...")
StringGadget(1,10,10,50,30,value)
n=1
v=1
w=255
Debug "#"+Str(n)+" = "+Str(GetGadgetState(n))+" ["+Str(v)+".."+Str(w)+"] -> "+Str(Max(Min(Val(GetGadgetText(n)),w),v))
m=Val(GetGadgetText(n))
Debug "1a :) "+m
m=Min(m,w)
Debug "1b :) "+m
m=Max(m,v)
Debug "1c :) "+m
Debug "2 :) "+Str(Max(Min(Val(GetGadgetText(n)),w),v))
m=Max(Min(Val(GetGadgetText(n)),w),v)
Debug "3 :( "+Str(m)
m=Max(Min(Val(value),w),v)
Debug "4 :( "+Str(m)
n=Val(value)
m=Max(Min(n,w),v)
Debug "5 :) "+Str(m)
Simplified (removed the GetGadgetText) the snippet to reduce the macro overhead...
Code: Select all
Macro Min(a,b)
(Bool(a <= b) * a + Bool(b < a) * b)
EndMacro
Macro Max(a,b)
(Bool(a >= b) * a + Bool(b > a) * b)
EndMacro
value.s="111"
n=1
v=1
w=255
m=Max(Min(Val(value),w),v)
Debug m
;m=(Bool((Bool(Val(GetGadgetText(n)) <= w) * 111 + Bool(w < 111) * w) >= v) * (Bool(Val(GetGadgetText(n)) <= w) * 111 + Bool(w < 111) * w) + Bool(v > (Bool(111 <= w) * 111 + Bool(w < 111) * w)) * v)
m=(Bool((Bool(Val(value) <= w) * 111 + Bool(w < 111) * w) >= v) * (Bool(Val(value) <= w) * 111 + Bool(w < 111) * w) + Bool(v > (Bool(111 <= w) * 111 + Bool(w < 111) * w)) * v)
Debug m
;m=Bool(Bool(Val(GetGadgetText(n)) <= w) * 111 >= v) * (Bool(Val(GetGadgetText(n)) <= w) * 111)
m=Bool(Bool(Val(value) <= 255) * 111 >= 1) * (Bool(Val(value) <= 255) * 111)
Debug m
m=Bool(Bool(111 <= 255) * 111 >= 1) * (Bool(Val(value) <= 255) * 111)
Debug m
m=Bool(Bool(Val(value) <= 255) * 111 >= 1) * (Bool(111 <= 255) * 111)
Debug m
Re: Max() and Min(), procedure and macro variants.
Posted: Wed Jun 28, 2023 4:11 pm
by NicTheQuick
These macros are not meant for use with complex parameters like other functions or terms because they always will be evaluated twice.
Today with the C backend you should just use procedures and enable optimizations. The compiler will inline such simple things automatically for you.
Example code to count evaluations:
Code: Select all
Macro Min(a,b)
(Bool((a) <= (b)) * (a) + Bool((b) < (a)) * (b))
EndMacro
Macro Max(a,b)
(Bool((a) >= (b)) * (a) + Bool((b) > (a)) * (b))
EndMacro
Global a_evaluated.i = 0
Procedure.i a(a.i)
a_evaluated + 1
ProcedureReturn a
EndProcedure
Global b_evaluated.i = 0
Procedure.i b(b.i)
b_evaluated + 1
ProcedureReturn b
EndProcedure
Debug Min(a(5), b(19))
Debug "a was evaluated " + a_evaluated + " times."
Debug "b was evaluated " + b_evaluated + " times."
Re: Max() and Min(), procedure and macro variants.
Posted: Wed Jun 28, 2023 8:30 pm
by Michael Vogel
Yes, a "solution" is the C-Backend or native Min/Max/Abs functions or procedures or....
...but I'd like to know what's going on here to avoid such situations.
I don't see that the macro is responsible for the issue as shown in the second code which produces a wrong result without using the macros.
Re: Max() and Min(), procedure and macro variants.
Posted: Wed Jun 28, 2023 9:57 pm
by NicTheQuick
Well, I only have version 5.72 installed in parallel, but I don't use x86 at all because I have no 32 bit libraries installed and it makes no sense to me in general.
I think the most important thing is that your code works fine with 5.72 x64 and also with 6.02 x64, right?
Re: Max() and Min(), procedure and macro variants.
Posted: Wed Jun 28, 2023 11:11 pm
by Michael Vogel
As said, there are also workarounds for the x86 compiler, the unpleasent thing for me is that a code like...
m=Bool(Bool(Val(value) <= 255) * 111 >= 1) * (Bool(Val(value) <= 255) * 111)
does return different values when using different compiler platforms.
Re: Max() and Min(), procedure and macro variants.
Posted: Thu Jun 29, 2023 10:37 am
by Mijikai
x64 obj-code for Min, Max, Abs (compile with fasm):
Code: Select all
format MS64 COFF
public iMin
public iMax
public iAbs
section '.text' code readable executable
iMin:
cmp rcx,rdx
jl @f
mov rax,rdx
ret
@@:
mov rax,rcx
ret
iMax:
cmp rcx,rdx
jg @f
mov rax,rdx
ret
@@:
mov rax,rcx
ret
iAbs:
mov rax,rcx
shl rcx,0x1
cmp rax,rcx
jl @f
neg rax
@@:
ret
Then it can be used in PB like this:
Code: Select all
EnableExplicit
Import "minmax.obj"
iMin.i(ValueA.i,ValueB.i)
iMax.i(ValueA.i,ValueB.i)
iAbs.i(Value.i)
EndImport
Debug iMin(10,-20)
Debug iMin(-20,10)
Debug iMax(10,-20)
Debug iMax(-20,10)
Debug iAbs(1110)
Debug iAbs(-123)
End
should work with the C-backend
Re: Max() and Min(), procedure and macro variants.
Posted: Thu Jun 29, 2023 5:56 pm
by Michael Vogel
I don't feel fine posting here as the issue seems to be related to Bool() and not to macros, but I didn't saw that in the first moment
Anyhow all tested
x86 versions (5.x and 6.x) show a result of
12321 and all
x64 versions the correct result
111 for the following code:
Code: Select all
value.s="111"
m=Bool(Bool(Val(value) <= 255) * 111 >= 1) * (Bool(Val(value) <= 255) * 111)
Debug m
Re: Max() and Min(), procedure and macro variants.
Posted: Fri Jun 30, 2023 1:28 am
by Demivec
Michael Vogel wrote: Thu Jun 29, 2023 5:56 pm
I don't feel fine posting here as the issue seems to be related to Bool() and not to macros, but I didn't saw that in the first moment
Anyhow all tested
x86 versions (5.x and 6.x) show a result of
12321 and all
x64 versions the correct result
111 for the following code:
Code: Select all
value.s="111"
m=Bool(Bool(Val(value) <= 255) * 111 >= 1) * (Bool(Val(value) <= 255) * 111)
Debug m
Here's the assembly for a v6.02 LTS x86 and x64 compilation for comparison:
Code: Select all
;
; PureBasic 6.02 LTS (Windows - x86) generated code
; m=Bool(Bool(Val(value) <= 255) * 111 >= 1) * (Bool(Val(value) <= 255) * 111)
PUSH dword [v_value]
CALL _PB_Val@4
PUSH edx
PUSH eax
QuadCompare0:
POP ebx
POP edi
CMP edi,0
JG .Ok
JL .Cancel
CMP ebx,255
JA .Ok
.Cancel:
XOR eax,eax
JMP .End
.Ok:
MOV eax,1
.End:
OR eax,eax
_Bool0:
JNE .False
MOV eax,1
JMP .True
.False:
XOR eax,eax
.True:
MOV ebx,eax
IMUL ebx,111
CMP ebx,1
_Bool1:
JL .False
MOV eax,1
JMP .True
.False:
XOR eax,eax
.True:
MOV ebx,eax
PUSH dword [v_value]
CALL _PB_Val@4
PUSH edx
PUSH eax
QuadCompare1:
POP ebx
POP edi
CMP edi,0
JG .Ok
JL .Cancel
CMP ebx,255
JA .Ok
.Cancel:
XOR eax,eax
JMP .End
.Ok:
MOV eax,1
.End:
OR eax,eax
_Bool2:
JNE .False
MOV eax,1
JMP .True
.False:
XOR eax,eax
.True:
MOV edi,eax
IMUL edi,111
IMUL ebx,edi
MOV dword [v_m],ebx
; Debug m
_PB_EOP_NoValue:
PUSH dword 0
_PB_EOP:
CALL _PB_EndFunctions
CALL _SYS_FreeStrings@0
PUSH dword [PB_MemoryBase]
CALL _HeapDestroy@4
CALL _ExitProcess@4
_PB_EndFunctions:
CALL _PB_FreeMemorys@0
RET
;
Code: Select all
;
; PureBasic 6.02 LTS (Windows - x64) generated code
; m=Bool(Bool(Val(value) <= 255) * 111 >= 1) * (Bool(Val(value) <= 255) * 111)
PUSH qword [v_value]
POP rcx
CALL PB_Val
MOV r15,rax
CMP r15,255
_Bool0:
JG .False
MOV rax,1
JMP .True
.False:
XOR rax,rax
.True:
MOV r15,rax
IMUL r15,111
CMP r15,1
_Bool1:
JL .False
MOV rax,1
JMP .True
.False:
XOR rax,rax
.True:
MOV r15,rax
PUSH qword [v_value]
POP rcx
CALL PB_Val
MOV r14,rax
CMP r14,255
_Bool2:
JG .False
MOV rax,1
JMP .True
.False:
XOR rax,rax
.True:
MOV r14,rax
IMUL r14,111
IMUL r15,r14
MOV qword [v_m],r15
; Debug m
_PB_EOP:
CALL PB_EndFunctions
CALL SYS_FreeStrings
MOV rcx,[PB_MemoryBase]
CALL HeapDestroy
MOV rcx,[PB_ExitCode]
CALL ExitProcess
PB_EndFunctions:
SUB rsp,40
CALL PB_FreeMemorys
ADD rsp,40
RET
;
Re: Max() and Min(), procedure and macro variants.
Posted: Fri Apr 25, 2025 9:18 am
by Michael Vogel
Just to recheck the possibility to speed up small things...
For me it seems that using macros are still the fastest solution in most cases (Windows 11, 64 Bit, PB5.73 & 6.04), even it's code does not look very smooth:
Code: Select all
; Standard Procedure
Procedure.i Limit(i)
If i<255
ProcedureReturn i
Else
ProcedureReturn 255
EndIf
EndProcedure
; CallFunctionFast
*CallLimit=@Limit()
; ProtoType
Prototype.i ProtoTypeLimit(param)
PtypLimit.ProtoTypeLimit
PtypLimit=@Limit()
; Macro
Macro McroLimit(a)
(Bool(a < 255) * a + Bool(255 < a) * 255)
EndMacro
Macro IfElse(relation,true,false)
Bool(relation)*true + Bool(Not(relation))*false
EndMacro
t-ElapsedMilliseconds()
For n=0 To 9999999
a=0
For i=205 To 305 Step 10
a+Limit(i)
;a+CallFunctionFast(*CallLimit,i)
;a+PtypLimit(i)
;a+McroLimit(i)
;a+IfElse(i<255,i,255)
Next i
Next n
t+ElapsedMilliseconds()
MessageRequester(Str(a),Str(t)+"ms")