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