Max() and Min(), procedure and macro variants.

Share your advanced PureBasic knowledge/code with the community.
User avatar
skywalk
Addict
Addict
Posts: 4003
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Max() and Min(), procedure and macro variants.

Post 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
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
Shield
Addict
Addict
Posts: 1021
Joined: Fri Jan 21, 2011 8:25 am
Location: 'stralia!
Contact:

Re: Max() and Min(), procedure and macro variants.

Post by Shield »

So the minimum value is always 0 or 1? :wink:
Image
Blog: Why Does It Suck? (http://whydoesitsuck.com/)
"You can disagree with me as much as you want, but during this talk, by definition, anybody who disagrees is stupid and ugly."
- Linus Torvalds
User avatar
Demivec
Addict
Addict
Posts: 4091
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Max() and Min(), procedure and macro variants.

Post 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
User avatar
skywalk
Addict
Addict
Posts: 4003
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Max() and Min(), procedure and macro variants.

Post by skywalk »

Sorry, I didn't run the code :oops: :lol:
Thanks Demivec :)
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
WilliamL
Addict
Addict
Posts: 1224
Joined: Mon Aug 04, 2008 10:56 pm
Location: Seattle, USA

Re: Max() and Min(), procedure and macro variants.

Post 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
Last edited by WilliamL on Sat Feb 23, 2013 1:42 am, edited 2 times in total.
MacBook Pro-M1 (2021), Sonoma 14.4.1, PB 6.10LTS M1
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Re: Max() and Min(), procedure and macro variants.

Post by Rescator »

Updated
User avatar
Michael Vogel
Addict
Addict
Posts: 2680
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Max() and Min(), procedure and macro variants.

Post 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

User avatar
NicTheQuick
Addict
Addict
Posts: 1227
Joined: Sun Jun 22, 2003 7:43 pm
Location: Germany, Saarbrücken
Contact:

Re: Max() and Min(), procedure and macro variants.

Post 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."
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
User avatar
Michael Vogel
Addict
Addict
Posts: 2680
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Max() and Min(), procedure and macro variants.

Post 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.
User avatar
NicTheQuick
Addict
Addict
Posts: 1227
Joined: Sun Jun 22, 2003 7:43 pm
Location: Germany, Saarbrücken
Contact:

Re: Max() and Min(), procedure and macro variants.

Post 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?
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
User avatar
Michael Vogel
Addict
Addict
Posts: 2680
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Max() and Min(), procedure and macro variants.

Post 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.
User avatar
Mijikai
Addict
Addict
Posts: 1360
Joined: Sun Sep 11, 2016 2:17 pm

Re: Max() and Min(), procedure and macro variants.

Post 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
User avatar
Michael Vogel
Addict
Addict
Posts: 2680
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Max() and Min(), procedure and macro variants.

Post 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
User avatar
Demivec
Addict
Addict
Posts: 4091
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Max() and Min(), procedure and macro variants.

Post 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
; 
Post Reply