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. :wink:

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? :wink:

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 :oops: :lol:
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... :D

[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 :shock:

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 :shock:

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")