Page 3 of 5

Re: Absolute Val for Integers?

Posted: Wed Jan 23, 2019 7:18 am
by wilbert
I don't see a reason for a $8000000000000000 exception as the problem is not really with the abs function.
If you look at the input value as a signed integer (which it is) and the output value as an unsigned integer (which it is) the returned value is fine.
The main problem is that PB doesn't support unsigned integers.
If you format the output as an unsigned integer, it will show 9223372036854775808.

Re: Absolute Val for Integers?

Posted: Wed Jan 23, 2019 10:28 am
by idle
wilbert wrote:I don't see a reason for a $8000000000000000 exception as the problem is not really with the abs function.
If you look at the input value as a signed integer (which it is) and the output value as an unsigned integer (which it is) the returned value is fine.
The main problem is that PB doesn't support unsigned integers.
If you format the output as an unsigned integer, it will show 9223372036854775808.
I don't think it's an exception either but it just means you need to check the result is > 0
it's still a valid point though and if you wanted to do a bool comparison with it wrapping it to 0 makes sense
even so it's still not ideal but as an inline macro it's not to far from the optimal solution.

Code: Select all

Macro IABS(v) ;in place no branching and gets peep hole optimized with overflow to 0 
   (((v) ! ((v)>>((SizeOf(integer)<<3)-1))) - ((v)>>((SizeOf(integer)<<3)-1))) ;& ($8000000000000000-1)
EndMacro

Re: Absolute Val for Integers?

Posted: Wed Jan 23, 2019 12:21 pm
by Josh
Why should AbsI check for an exception? You already have the same problem with basic arithmetical operation:

Code: Select all

z.q = -9223372036854775808
Debug z
Debug 0 - z
Debug z - 1
To 99.99%, a program will not come into this range and if a programmer believes that this is a critical area for him, he must check the value by himself.


@Idle
That doesn't run at x86:

Code: Select all

Macro IABS(v) ;in place no branching and gets peep hole optimized with overflow to 0 
   (((v) ! ((v)>>((SizeOf(integer)<<3)-1))) - ((v)>>((SizeOf(integer)<<3)-1))) ;& ($8000000000000000-1)
EndMacro

                                 ; x86                 x64
Debug       $8000000000000000-1  ; 9223372036854775807 9223372036854775807
Debug Iabs ($8000000000000000-1) ; 9223372028264841217 9223372036854775807

Re: Absolute Val for Integers?

Posted: Wed Jan 23, 2019 1:06 pm
by Olliv
Good luck

Re: Absolute Val for Integers?

Posted: Wed Jan 23, 2019 9:16 pm
by idle
@josh
The macro will work fine for integers but it won't work for quads on x86 and you can ignore the code after the comment.
that was for discussion on what to do on the range limit.

Re: Absolute Val for Integers?

Posted: Thu Jan 24, 2019 2:16 pm
by Little John
wilbert wrote:As a macro ... :)

Code: Select all

Macro _Abs(n)
  ((n)*(Bool((n)>0)*2-1))
EndMacro

Debug _Abs(100 - 110564567689766987)
Debug _Abs(-2.75)
Cool. 8)
And pretty fast. Thank you!
I wonder why the built-in Abs() function is not implemented this way.

Re: Absolute Val for Integers?

Posted: Thu Jan 24, 2019 2:36 pm
by NicTheQuick
Little John wrote:
wilbert wrote:As a macro ... :)

Code: Select all

Macro _Abs(n)
  ((n)*(Bool((n)>0)*2-1))
EndMacro

Debug _Abs(100 - 110564567689766987)
Debug _Abs(-2.75)
Cool. 8)
And pretty fast. Thank you!
I wonder why the built-in Abs() function is not implemented this way.
Because there are way too many operations taking place: one comparison, two multiplications and one substraction. Also the paramater gets called twice which is a terrible idea when the parameter is a function call. There is a single ASM instruction which can convert a value to its absolute value. That would be the correct way for a built-in function.

Re: Absolute Val for Integers?

Posted: Thu Jan 24, 2019 3:14 pm
by wilbert
NicTheQuick wrote:There is a single ASM instruction which can convert a value to its absolute value. That would be the correct way for a built-in function.
That is the way the built-in function works but that single ASM instruction (fabs) operates on floating point values, not integer values.
For 32 bit integers it is accurate enough but for 64 bit integers it isn't. That is what is made clear in the first post from this thread.
A single ASM instruction to get the absolute value of a 64 bit integer value doesn't exist.

Re: Absolute Val for Integers?

Posted: Thu Jan 24, 2019 3:30 pm
by NicTheQuick
Making a float value positive is easy because you only have to clear one single bit.

But I didn't expect that there is no ASM instruction to make an integer positive. :shock:

Was this method already mentioned here? But the problem remains that v is called twice.

Code: Select all

Macro Abs_(v)
	((((v) >> 62) | 1) * (v))
EndMacro

a = -1876543

a = Abs_(a)

Debug a
Shitty IDE doesn't let me work anymore.

Re: Absolute Val for Integers?

Posted: Thu Jan 24, 2019 6:00 pm
by mk-soft
Only Integers

Update

Code: Select all

CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
  Procedure AbsI(value.i)
    ! mov rax, qword[p.v_value]
    ! bt rax, 63
    ! jnb __absi_no
    ! not rax
    ! add rax, 1
    ! __absi_no:
    ProcedureReturn
  EndProcedure
CompilerElse
  Procedure AbsI(value.i)
    ! mov eax, dword[p.v_value]
    ! bt eax, 31
    ! jnb __absi_no
    ! not eax
    ! add eax, 1
    ! __absi_no:
    ProcedureReturn
  EndProcedure
CompilerEndIf

qVal.i = -2100
Debug AbsI(qVal)

lVal.l = -2200
Debug AbsI(lVal)

Re: Absolute Val for Integers?

Posted: Thu Jan 24, 2019 7:54 pm
by wilbert
A small comparison between different ASM methods (x64).
On my computer there's very little difference in speed.

Code: Select all

Procedure AbsI(value.i)
  ! mov rax, qword[p.v_value]
  ! bt rax, 63
  ! jnb __absi_no
  ! not rax
  ! add rax, 1
  ! __absi_no:
  ProcedureReturn
EndProcedure

Procedure AbsI_2(value.i)
  !mov rax, [p.v_value]
  !cqo
  !add rax, rdx
  !xor rax, rdx
  ProcedureReturn
EndProcedure

Procedure AbsI_3(value.i)
  !mov rax, [p.v_value]
  !neg rax
  !cmovs rax, [p.v_value]
  ProcedureReturn
EndProcedure

n = 12345678

t1 = ElapsedMilliseconds()
For i=1 To 200000000
  r = AbsI(n)
Next
t2 = ElapsedMilliseconds()
For i=1 To 200000000
  r = AbsI_2(n)
Next
t3 = ElapsedMilliseconds()
For i=1 To 200000000
  r = AbsI_3(n)
Next
t4 = ElapsedMilliseconds()

MessageRequester("Results", Str(t2-t1)+" vs "+Str(t3-t2)+" vs "+Str(t4-t3))

Re: Absolute Val for Integers?

Posted: Thu Jan 24, 2019 8:34 pm
by Little John
NicTheQuick wrote:
Little John wrote:I wonder why the built-in Abs() function is not implemented this way.
Also the paramater gets called twice which is a terrible idea when the parameter is a function call.
I agree. However, this is only because that macro is written in a way so that it can be called like a function. A built-in function doesn't have to work exactly like this, but could copy the value of the parameter to a variable.
wilbert wrote:A small comparison between different ASM methods (x64).
On my computer there's very little difference in speed.
Same here on my computer.
And even a bit faster than any of these three procedures (using the same test code as you did) is

Code: Select all

Macro _Abs(n)
  ((n)*(Bool((n)>0)*2-1))
EndMacro
:-)

Re: Absolute Val for Integers?

Posted: Thu Jan 24, 2019 8:53 pm
by Josh
I don't know if I completely misunderstand something here, but what about a simple 0-x?

Code: Select all

Procedure AbsI(value.i)
  ! mov rax, qword[p.v_value]
  ! bt rax, 63
  ! jnb __absi_no
  ! not rax
  ! add rax, 1
  ! __absi_no:
  ProcedureReturn
EndProcedure

Procedure AbsI_2(value.i)
  !mov rax, [p.v_value]
  !cqo
  !add rax, rdx
  !xor rax, rdx
  ProcedureReturn
EndProcedure

Procedure AbsI_3(value.i)
  !mov rax, [p.v_value]
  !neg rax
  !cmovs rax, [p.v_value]
  ProcedureReturn
EndProcedure

n = 12345678

t1 = ElapsedMilliseconds()
For i=1 To 200000000
  r = AbsI(n)
Next
t2 = ElapsedMilliseconds()
For i=1 To 200000000
  r = AbsI_2(n)
Next
t3 = ElapsedMilliseconds()
For i=1 To 200000000
  r = AbsI_3(n)
Next
t4 = ElapsedMilliseconds()
For i=1 To 200000000
  If n < 0
    n = 0 - n
  EndIf
Next
t5 = ElapsedMilliseconds()

MessageRequester("Results", Str(t2-t1)+" vs "+Str(t3-t2)+" vs "+Str(t4-t3)+" vs "+Str(t5-t4))

Re: Absolute Val for Integers?

Posted: Thu Jan 24, 2019 9:33 pm
by mk-soft
I added AbsQ(Value)

Update
- Added wilberts code

Code: Select all

;-TOP
; AbsI and AbsQ
; By mk-soft and wilbert from 24.01.2019

CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
  Procedure AbsI(value.i)
    !mov rax, [p.v_value]
    !neg rax
    !cmovs rax, [p.v_value]
    ProcedureReturn
  EndProcedure
CompilerElse
  Procedure AbsI(value.i)
    !mov eax, [p.v_value]
    !neg eax
    !cmovs eax, [p.v_value]
    ProcedureReturn
  EndProcedure
CompilerEndIf

CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
  Procedure.q AbsQ(value.q)
    !mov rax, [p.v_value]
    !neg rax
    !cmovs rax, [p.v_value]
    ProcedureReturn
  EndProcedure
CompilerElse
  Procedure.q AbsQ(value.q)
    ! lea    eax,dword[p.v_value]
    ! mov    edx,dword[eax+4]
    ! mov    eax,dword[eax]
    ! bt edx, 31
    ! jnb __absq_no
    ! not    eax
    ! not    edx
    ! add    eax,1
    ! adc    edx,0
    ! __absq_no:
    ProcedureReturn
  EndProcedure
CompilerEndIf

;-

Define iVal.i = $80000001

Debug AbsI(iVal)

Define qVal.q = $8000000000000001

r1.q = AbsQ(qVal)
Debug r1
Debug Hex(r1)

Re: Absolute Val for Integers?

Posted: Thu Jan 24, 2019 9:41 pm
by mk-soft
P.S.
The third method by Wilbert is the fastest . The fourth method cannot return a return value and is therefore invalid.