Strange behavior of Abs()! @PureBasicTeam

Just starting out? Need help? Post your questions and find answers here.
SMaag
Enthusiast
Enthusiast
Posts: 303
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Strange behavior of Abs()! @PureBasicTeam

Post by SMaag »

we found out a strange behavior of the Abs() function.

It is a Float function, but can we use it for big Quads too? More than 53Bits!

Is it internal overladed with a AbsQ() function which is not documented?

The problem you can see at Q= -100000000000000005

Abs(-100000000000000005) -> 100000000000000000.0
But if we use with a quad variable the result is 100000000000000005
what is correct for a quad!

Code: Select all

EnableExplicit

Macro mac_AbsInt(val)
  If val < 0 : val = -val : EndIf  
EndMacro

Define.q Q, r, s

Q= -100000000000000005


r = Abs(Q)
s = Q
mac_AbsInt(s)

Debug r
Debug s
Debug Abs(Q)
User avatar
jacdelad
Addict
Addict
Posts: 1993
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: Strange behavior of Abs()! @PureBasicTeam

Post by jacdelad »

I may be wrong, but since a.q=Abs(b.q) works, maybe it's a problem with Debug, not Abs()???
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
User avatar
mk-soft
Always Here
Always Here
Posts: 6207
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Strange behavior of Abs()! @PureBasicTeam

Post by mk-soft »

The ABS function is a float / double function. This means that it cannot be accurate for quad, as the quad is first converted into a double and then back into a quad.

Code: Select all

// r = Abs(Q)
double r0=PB_Abs(v_q);
v_r=SYS_BankerRoundQuad(r0);
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
STARGÅTE
Addict
Addict
Posts: 2227
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: Strange behavior of Abs()! @PureBasicTeam

Post by STARGÅTE »

I fully agree with mk-soft, Abs() is a floating point function. However, I wonder, why it works for quads as well (without quad->double->quad conversion) in the ASM backend with explicit type definition:

Code: Select all

Define Quad.q = -$7FFFFFFFFFFFDEAD
Quad = Abs(Quad)
Debug Hex(Quad)
ASM backend wrote:7FFFFFFFFFFFDEAD
C backend wrote:7FFFFFFFFFFFE000
So there must be a hidden function overloading in case of the ASM backend.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
SMaag
Enthusiast
Enthusiast
Posts: 303
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: Strange behavior of Abs()! @PureBasicTeam

Post by SMaag »

Maybe the following explains the correct working of ABS() for Quads

if we look at the assembly output

Code: Select all

x.i = -100000000000000005
y.i = Abs(x)

; y.i = Abs(x)
  FILD   dword [v_x]
  FABS
  FISTP  dword [v_y]

here the documentation of the ASM commands

FILD
Converts the signed-integer source operand into double extended-precision floating-point format and pushes the value onto the FPU register stack. The source operand can be a word, doubleword, or quadword integer. It is loaded without rounding errors. The sign of the source operand is preserved.

In the Intel Manual I found:
The x87 FPU recognizes and operates on the following seven data types (see Figures 8-13): single precision
floating-point, double precision floating-point, double extended precision floating-point, signed word integer,
signed doubleword integer, signed quadword integer, and packed BCD decimal integers.
(https://cdrdv2-public.intel.com/671436/ ... -vol-1.pdf
page 215)


My conclusion is: it is a debug problem!
because x87 Floating Point are probably 80Bit (64Bit Fraction, 15Bit Exponent an 1 Sign, so the FPU can store the complete 64Bit of a Quad without rounding)

For x64 Processors we can use ABS() for Quads without a rounding error!
User avatar
mk-soft
Always Here
Always Here
Posts: 6207
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Strange behavior of Abs()! @PureBasicTeam

Post by mk-soft »

Not good,

not on C-Backend !
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
juergenkulow
Enthusiast
Enthusiast
Posts: 581
Joined: Wed Sep 25, 2019 10:18 am

Re: Strange behavior of Abs()! @PureBasicTeam

Post by juergenkulow »

Code: Select all

; Abs in C backend changes values
q.q=-Random(100000000000000006,100000000000000005)
absq.q=Abs(q)
Debug q
Debug absq
; ASM Backend
; -100000000000000006
; 100000000000000006
; C Backend 
; -100000000000000006
; 100000000000000000
;                  ^
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Strange behavior of Abs()! @PureBasicTeam

Post by wilbert »

With the c backend you could also use llabs

Code: Select all

int64.q = -100000000000000005
!v_int64 = llabs(v_int64);
Debug int64
Windows (x64)
Raspberry Pi OS (Arm64)
SMaag
Enthusiast
Enthusiast
Posts: 303
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: Strange behavior of Abs()! @PureBasicTeam

Post by SMaag »

Oh shit!
I feel like I opepend "Pandora's Box"!

It would be time for a PB integrated AbsInt() function
User avatar
Kiffi
Addict
Addict
Posts: 1485
Joined: Tue Mar 02, 2004 1:20 pm
Location: Amphibios 9

Re: Strange behavior of Abs()! @PureBasicTeam

Post by Kiffi »

SMaag wrote: Thu Jan 04, 2024 11:02 amIt would be time for a PB integrated AbsInt() function
Skål (scnr)
Hygge
SMaag
Enthusiast
Enthusiast
Posts: 303
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: Strange behavior of Abs()! @PureBasicTeam

Post by SMaag »

Really a difference in ASM and C-Backend

Code: Select all

Macro mac_AbsQ1(number)
  (number ! (number >> 63) + (number >> 63) &1)
EndMacro

Macro mac_AbsQ2(number)
    ((number + (number >> 63)) ! (number >> 63))  
EndMacro

Procedure AbsQ(number)
  If number < 0
    ProcedureReturn -number
  EndIf
  ProcedureReturn number  
EndProcedure


Define I, K, N, t1

t1 = ElapsedMilliseconds()

#Start = 100000000000000005

For I = #Start To #Start +32
  Debug " "
  K = -I
  N = mac_AbsQ1(K)
  Debug "AbsQ1 = " + N
  
  N = mac_AbsQ2(K)
  Debug "AbsQ2 = " + N
  
  N = AbsQ(K)
  Debug "AbsQ = " + N
  
  ; Abs() is different in ASM and C-Backend
  N = Abs(K)
  Debug "Abs() = " + N

Next

t1 = ElapsedMilliseconds() - t1

MessageRequester("Time", Str(t1))

here the C Version Output => not correct! With ASM it's correct!

AbsQ1 = 100000000000000005
AbsQ2 = 100000000000000005
AbsQ = 100000000000000005
Abs() = 100000000000000000

AbsQ1 = 100000000000000006
AbsQ2 = 100000000000000006
AbsQ = 100000000000000006
Abs() = 100000000000000000

AbsQ1 = 100000000000000007
AbsQ2 = 100000000000000007
AbsQ = 100000000000000007
Abs() = 100000000000000000

AbsQ1 = 100000000000000008
AbsQ2 = 100000000000000008
AbsQ = 100000000000000008
Abs() = 100000000000000000

AbsQ1 = 100000000000000009
AbsQ2 = 100000000000000009
AbsQ = 100000000000000009
Abs() = 100000000000000016

AbsQ1 = 100000000000000010
AbsQ2 = 100000000000000010
AbsQ = 100000000000000010
Abs() = 100000000000000016

AbsQ1 = 100000000000000011
AbsQ2 = 100000000000000011
AbsQ = 100000000000000011
Abs() = 100000000000000016

AbsQ1 = 100000000000000012
AbsQ2 = 100000000000000012
AbsQ = 100000000000000012
Abs() = 100000000000000016

AbsQ1 = 100000000000000013
AbsQ2 = 100000000000000013
AbsQ = 100000000000000013
Abs() = 100000000000000016

AbsQ1 = 100000000000000014
AbsQ2 = 100000000000000014
AbsQ = 100000000000000014
Abs() = 100000000000000016

AbsQ1 = 100000000000000015
AbsQ2 = 100000000000000015
AbsQ = 100000000000000015
Abs() = 100000000000000016

AbsQ1 = 100000000000000016
AbsQ2 = 100000000000000016
AbsQ = 100000000000000016
Abs() = 100000000000000016

AbsQ1 = 100000000000000017
AbsQ2 = 100000000000000017
AbsQ = 100000000000000017
Abs() = 100000000000000016
User avatar
STARGÅTE
Addict
Addict
Posts: 2227
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: Strange behavior of Abs()! @PureBasicTeam

Post by STARGÅTE »

SMaag wrote: Thu Jan 04, 2024 1:19 pm Really a difference in ASM and C-Backend
[...]
here the C Version Output => not correct! With ASM it's correct!
Sure, from the mathematical point of view the ASM backend is correct.
However, I would say, it is a bug in the ASM backend, not in the C backend.
In the documentation it is clearly written, Result.d = Abs(Number.d).
This means, also the ASM backend has to perform a type cast from Quad to Double to Quad.

There was a similar bug in older PB versions with trigonometric functions.
This code has given "Unsame" because y was calculated with double precision (64 bit), in the comparison, however, Cos(x) as well as the test on equality was performed with FPU 80 bit precision.

Code: Select all

Define x.d = 0.2
Define y.d = Cos(x)

If y = Cos(x)
	Debug "Same"
Else
	Debug "Unsame"
EndIf
This was fixed now in PB 6.0.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
SMaag
Enthusiast
Enthusiast
Posts: 303
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: Strange behavior of Abs()! @PureBasicTeam

Post by SMaag »

Sure, from the mathematical point of view the ASM backend is correct.
However, I would say, it is a bug in the ASM backend, not in the C backend.
In the documentation it is clearly written, Result.d = Abs(Number.d).
This means, also the ASM backend has to perform a type cast from Quad to Double to Quad.
I don't think it is a bug! Not in the ASM and not in the C-Backend. It's a different handling because C
has it's own optimation routines!

in the ASM Backend
the FILD command is used to load the integer value to the floating point registers.
The Intel documentation says
The source operand can be a word, doubleword, or quadword integer. It is loaded without rounding errors. The sign of the source operand is preserved.
So when using the FILD command there is no rounding!

C use an other methode for the Abs()
I did an IDA disassemble of the code

Code: Select all

s.s ="123"
x.q = 7
y.q = Abs(x)

MessageRequester("Result", "Abs() = " + Y)

; here the disassembley from C-Backend

mov     cs:qword_140005730, 7
movsd   xmm0, cs:qword_140003140
call    sub_140001A90

sub_140001A90 proc near
andps   xmm0, cs:xmmword_140003160
retn
sub_140001A90 endp

xmmword_140003160 xmmword 7FFFFFFFFFFFFFFF7FFFFFFFFFFFFFFFh
C use ANDPS Bitwise Logical AND of Packed Single Precision Floating-Point Values

so the Abs() is
ANDPS number, 7FFFFFFFFFFFFFFF7FFFFFFFFFFFFFFFh

This don't work for Integers, doesnt matter 32 or 64 Bit

I do not understand what should be the adbantage of this
For me it seems slower than FILD
SMaag
Enthusiast
Enthusiast
Posts: 303
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: Strange behavior of Abs()! @PureBasicTeam

Post by SMaag »

if we use in PB the ABS() with an Integer Value, this is the disassembley

Code: Select all

call    sub_140001B30
mov     cs:qword_140005730, 7
movsd   xmm0, cs:qword_140003140
call    sub_140001A90  ; what is a simple andps   xmm0, 7FFFFFFFFFFFFFFF7FFFFFFFFFFFFFFFh
movsd   [rsp+38h+var_18], xmm0

; after removing the sing bit
; the value is pused on the float stack
fld     [rsp+38h+var_18]	  ; but this is not a float it is an integer			
fistp   [rsp+38h+var_10	; here we convert an Integer back to int! That can't be	
mov     rax, [rsp+38h+var_10]
if we use in PB the Abs() with a double value, This is the disassembley

Code: Select all

call    sub_140001CC0
movsd   xmm0, cs:qword_140003160
movsd   cs:qword_140005770, xmm0
call    sub_140001C20
movsd   cs:qword_140005768, xmm0 ; with doubles there is no converting back to Integer
for the ASM Backend we get this

Code: Select all


with the ASM Backend Code
  FILD   dword [v_x]	; here we load an integer to the FloatingPoint register and convert it to Float
  FABS
  FISTP  dword [v_y]	; convert back the Float to integer

Conclusion: PB handle the ABS() in the C-Backend different for Floating Point and Integers.
with intgers there is an additional command to convert back from float to int with this 2 commands

fld [rsp+38h+var_18] ; but this is not a float it is an integer ; !I was wrong, it is a float!
fistp [rsp+38h+var_10 ; here we convert an Integer back to int! That can't be !it's correct too!

but this is to much. It is still an integer and a conversion is not necessary!

Thats a BUG of the PB Compiler!
No it is not a Bug, see 2 posts later! It's a tricky 64-Bit float handling!
Last edited by SMaag on Thu Jan 04, 2024 8:31 pm, edited 2 times in total.
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Re: Strange behavior of Abs()! @PureBasicTeam

Post by Psychophanta »

My pi/2 cents of workarounded pragmatic solution for this issue:

Code: Select all

Macro absforints(v)
  Val(LTrim(Str(v#),"-"))
EndMacro
; OR if you prefer:
Procedure.q absforintsASM(v.q)
  !fild qword[p.v_v]
  !fabs
  !fistp qword[p.v_v]
  ProcedureReturn v
EndProcedure

debug absforints(-100000000000000005); :D
debug absforints(100000000000000005); :D

debug absforintsASM(-100000000000000005); :D
debug absforintsASM(100000000000000005); :D

Last edited by Psychophanta on Thu Jan 04, 2024 9:41 pm, edited 2 times in total.
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
Post Reply