Fastest way to wrap angle values (ASM)

Share your advanced PureBasic knowledge/code with the community.
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Fastest way to wrap angle values (ASM)

Post by Psychophanta »

Code updated For 5.20+

Useful asm functions to wrap angle values, signed and unsigned angles results:

Code: Select all

Procedure.f WrapAngle(angle.f); <- wraps a value into [0,2*Pi) fringe
  !fldpi; <- now i have pi into st0
  !fadd st0,st0; <- now i have 2*pi into st0
  !fld dword[p.v_angle]; <- now i have angle in st0, 2*pi into st1
  !fprem; <- now i have the remainder of angle/(2*pi) division (i.e. angle%(2*pi)) in st0, 2*pi in st1
  !fadd st1,st0;<- now i have angle%(2*pi) in st0, 2*pi+angle%(2*pi) into st1
  !fldz;<- now i have 0 in st0, angle%(2*pi) in st1, 2*pi+angle%(2*pi) into st2
  !fcomip st1; <- compare st0 and st1, and pop the stack, which means i have now angle%(2*pi) in st0, 2*pi+remainder into st1
  !fcmovnbe st0,st1; <- transfer st1 to st0 if not below or equal.
  !fstp st1; <- store st0 in st1 and pops stack, which means i have now the result in st0
  ProcedureReturn; <- return the result with this last pop
EndProcedure
Procedure.f WrapAngleSigned(angle.f); <- wraps a value into [-Pi,Pi] fringe
  !fldpi
  !fadd st0,st0; <- now i have 2*pi into st0
  !fld dword[p.v_angle]
  !fprem1
  !fstp st1
  ProcedureReturn
EndProcedure
Procedure.f WrapAngleDeg(angle.f); <- wraps a value into [0,360) fringe
  !fild dword[@f] ; <- now i have 360 into st0
  !fld dword[p.v_angle]
  !fprem
  !fadd st1,st0
  !fldz
  !fcomip st1
  !fcmovnbe st0,st1
  !fstp st1
  ProcedureReturn
  !@@:dd 360
EndProcedure
Procedure.f WrapAngleDegSigned(angle.f); <- wraps a value into [-180,180] fringe
  !fild dword[@f] ; <- now i have 360 into st0
  !fld dword[p.v_angle]
  !fprem1
  !fstp st1
  ProcedureReturn
  !@@:dd 360
EndProcedure
;It works in radians. 
;Usage examples for degrees: 
#DEGTORAD=#PI/180.0:#RADTODEG=180.0/#PI
; 
angle.f=45 
angleadd.f=-90 
angle.f=WrapAngle((angle.f+angleadd.f)*#DEGTORAD)*#RADTODEG 
Debug angle 
; 
angle.f=45 
angleadd.f=-90 
angle.f=WrapAngleSigned((angle.f+angleadd.f)*#DEGTORAD)*#RADTODEG 
Debug angle
EDIT: updated to PB4.0B7
Last edited by Psychophanta on Thu Mar 23, 2006 9:30 pm, edited 3 times in total.
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
User avatar
Comtois
Addict
Addict
Posts: 1431
Joined: Tue Aug 19, 2003 11:36 am
Location: Doubs - France

Post by Comtois »

thank you
Please correct my english
http://purebasic.developpez.com/
User avatar
Comtois
Addict
Addict
Posts: 1431
Joined: Tue Aug 19, 2003 11:36 am
Location: Doubs - France

Post by Comtois »

I am wrong or it is not faster ?

Code: Select all

Procedure.f WrapAngle(f1.f); <- wraps a value into [0,2*Pi) fringe
  !fldpi
  !fadd st0,st0; <- now i have 2*pi into st0
  !fld dword[esp]
  !fprem1
  !ftst ;test to see if modulo <= 0
  !fnstsw ax ;transfers FPU status word to ax
  !fwait
  !sahf   ;transfers ah to CPU flags.
  !jnc near @f ;if number has a negative value (modulo <= 0) then:
  !fadd st0,st1
  !@@:fstp st1
EndProcedure

Procedure.f WrapValue(Angle.f)
   Angle/360
     Angle-Int(Angle)
     If Angle<0
       ProcedureReturn (Angle+1)*360
     Else
       ProcedureReturn Angle*360
     EndIf
EndProcedure


;It works in radians.
;Usage examples for degrees:
#PI=3.14159265:#DEGTORAD=0.01745329:#RADTODEG=57.2957795
#Max= 1000000

;Premier Test
i.f=0
Tps=ElapsedMilliseconds()
While i < #Max
angleNew.f=WrapAngle(i*#DEGTORAD)*#RADTODEG
;angleNew.f=WrapAngle(i)
i + 0.1
Wend
Total1=ElapsedMilliseconds()-Tps


;
; ;Deuxième test
i.f=0
Tps=ElapsedMilliseconds()
While i < #Max
angleNew.f=WrapValue(i)
i + 0.1
Wend
Total2=ElapsedMilliseconds()-Tps

MessageRequester("Test","WrapAngle = " + Str(Total1) + #LFCR$ + "WrapValue = " + Str(Total2),0)
Last edited by Comtois on Fri Jan 13, 2006 3:35 pm, edited 1 time in total.
Please correct my english
http://purebasic.developpez.com/
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

About 344 vs 656 reported here.
I guess you are doing something wrong !

I'll add here a degree versions of both functions, i hope today :D
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
User avatar
Comtois
Addict
Addict
Posts: 1431
Joined: Tue Aug 19, 2003 11:36 am
Location: Doubs - France

Post by Comtois »

Psychophanta wrote:About 344 vs 656 reported here.
I guess you are doing something wrong !
i get
---------------------------
Test
---------------------------
WrapAngle = 875

WrapValue = 516
---------------------------
OK
---------------------------
Nothing wrong for me

Code: Select all

Procedure.f WrapAngle(f1.f); <- wraps a value into [0,2*Pi) fringe
  !fldpi
  !fadd st0,st0; <- now i have 2*pi into st0
  !fld dword[esp]
  !fprem1
  !ftst ;test to see if modulo <= 0
  !fnstsw ax ;transfers FPU status word to ax
  !fwait
  !sahf   ;transfers ah to CPU flags.
  !jnc near @f ;if number has a negative value (modulo <= 0) then:
  !fadd st0,st1
  !@@:fstp st1
EndProcedure

Procedure.f WrapValue(Angle.f)
   Angle/360
     Angle-Int(Angle)
     If Angle<0
       ProcedureReturn (Angle+1)*360
     Else
       ProcedureReturn Angle*360
     EndIf
EndProcedure


;It works in radians.
;Usage examples for degrees:
#PI=3.14159265:#DEGTORAD=0.01745329:#RADTODEG=57.2957795
#Max= 365

i.f=0

While i < #Max
Debug StrF(WrapAngle(i*#DEGTORAD)*#RADTODEG,3) + " / " + StrF(WrapValue(i),3)
i + 0.1
Wend
I'll add here a degree versions of both functions, i hope today :D
Hope too :)
Please correct my english
http://purebasic.developpez.com/
Pupil
Enthusiast
Enthusiast
Posts: 715
Joined: Fri Apr 25, 2003 3:56 pm

Post by Pupil »

you should disable the debugger around the procedures..
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

Comtois, it is very strange what happens there!
Can you try with these 2 and tell results?

Code: Select all

Procedure.f WrapAngle(angle.f); <- wraps a value into [0,2*Pi) fringe
  !fldpi
  !fadd st0,st0; <- now i have 2*pi into st0
  !fld dword[esp]
  !fprem1
  !ftst ;test to see if modulo <= 0
  !fstsw ax ;transfers FPU status word to ax
  !sahf   ;transfers ah to CPU flags.
  !jnc near @f ;if number has a negative value (modulo <= 0) then:
  !fadd st0,st1
  !@@:fstp st1
EndProcedure

Code: Select all

Procedure.f WrapAngle(angle.f); <- wraps a value into [0,2*Pi) fringe
  !fldpi
  !fadd st0,st0; <- now i have 2*pi into st0
  !fld dword[esp]
  !fprem1
  !fadd st1,st0
  !fldz
  !fcomip st1
  !fcmovnbe st0,st1
  !fstp st1
EndProcedure
Here is the ones with parameter in Degrees:

Code: Select all

Procedure.f WrapAngleDeg(angle.f); <- wraps a value into [0,360) fringe
  !fild dword[@f] ; <- now i have 360 into st0
  !fld dword[esp]
  !fprem
  !fadd st1,st0
  !fldz
  !fcomip st1
  !fcmovnbe st0,st1
  !fstp st1
  ProcedureReturn
  !@@:dd 360
EndProcedure
Procedure.f WrapAngleDegSigned(angle.f); <- wraps a value into [-180,180] fringe
  !fild dword[@f] ; <- now i have 360 into st0
  !fld dword[esp]
  !fprem1
  !fstp st1
  ProcedureReturn
  !@@:dd 360
EndProcedure

;Usage examples:
;
angle.f=45
angleadd.f=-90
angle.f=WrapAngleDeg(angle.f+angleadd.f)
Debug angle
;
angle.f=45
angleadd.f=-90
angle.f=WrapAngleDegSigned(angle.f+angleadd.f)
Debug angle
Last edited by Psychophanta on Sat Jan 14, 2006 12:21 am, edited 1 time in total.
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
User avatar
Comtois
Addict
Addict
Posts: 1431
Joined: Tue Aug 19, 2003 11:36 am
Location: Doubs - France

Post by Comtois »

here is my test
---------------------------
Test
---------------------------
WrapAngle1 = 891

WrapAngle2 = 890

WrapAngle3 = 875

WrapAngleDeg = 860

WrapValue = 500
---------------------------
OK
---------------------------

Code: Select all

DisableDebugger

Procedure.f WrapAngle1(angle.f); <- wraps a value into [0,2*Pi) fringe
  !fldpi
  !fadd st0,st0; <- now i have 2*pi into st0
  !fld dword[esp]
  !fprem1
  !ftst ;test to see if modulo <= 0
  !fstsw ax ;transfers FPU status word to ax
  !sahf   ;transfers ah to CPU flags.
  !jnc near @f ;if number has a negative value (modulo <= 0) then:
  !fadd st0,st1
  !@@:fstp st1
EndProcedure

Procedure.f WrapAngle2(angle.f); <- wraps a value into [0,2*Pi) fringe
  !fldpi
  !fadd st0,st0; <- now i have 2*pi into st0
  !fld dword[esp]
  !fprem1
  !fadd st1,st0
  !fldz
  !fcomip st1
  !fcmovnbe st0,st1
  !fstp st1
EndProcedure

Procedure.f WrapAngle3(f1.f); <- wraps a value into [0,2*Pi) fringe
  !fldpi
  !fadd st0,st0; <- now i have 2*pi into st0
  !fld dword[esp]
  !fprem1
  !ftst ;test to see if modulo <= 0
  !fnstsw ax ;transfers FPU status word to ax
  !fwait
  !sahf   ;transfers ah to CPU flags.
  !jnc near @f ;if number has a negative value (modulo <= 0) then:
  !fadd st0,st1
  !@@:fstp st1
EndProcedure

Procedure.f WrapAngleDeg(angle.f); <- wraps a value into [0,360) fringe
  !fild dword[@f] ; <- now i have 360 into st0
  !fld dword[esp]
  !fprem1
  !fadd st1,st0
  !fldz
  !fcomip st1
  !fcmovnbe st0,st1
  !fstp st1
  ProcedureReturn
  !@@:dd 360
EndProcedure 

Procedure.f WrapValue(Angle.f)
   ;Auteur Filperj
   Angle/360
     Angle-Int(Angle)
     If Angle<0
       ProcedureReturn (Angle+1)*360
     Else
       ProcedureReturn Angle*360
     EndIf
EndProcedure


;It works in radians.
;Usage examples for degrees:
#PI=3.14159265:#DEGTORAD=0.01745329:#RADTODEG=57.2957795
#Max= 1000000

;1er Test
i.f=0
Tps=ElapsedMilliseconds()
While i < #Max
	angleNew.f=WrapAngle1(i*#DEGTORAD)*#RADTODEG
	i + 0.1
Wend
Total1=ElapsedMilliseconds()-Tps

;2eme Test
i.f=0
Tps=ElapsedMilliseconds()
While i < #Max
	angleNew.f=WrapAngle2(i*#DEGTORAD)*#RADTODEG
	i + 0.1
Wend
Total2=ElapsedMilliseconds()-Tps


;3eme Test
i.f=0
Tps=ElapsedMilliseconds()
While i < #Max
	angleNew.f=WrapAngle3(i*#DEGTORAD)*#RADTODEG
	i + 0.1
Wend
Total3=ElapsedMilliseconds()-Tps

;4eme Test
i.f=0
Tps=ElapsedMilliseconds()
While i < #Max
	angleNew.f=WrapAngleDeg(i)
	i + 0.1
Wend
Total4=ElapsedMilliseconds()-Tps

;5eme test
i.f=0
Tps=ElapsedMilliseconds()
While i < #Max
	angleNew.f=WrapValue(i)
	i + 0.1
Wend
Total5=ElapsedMilliseconds()-Tps

MessageRequester("Test","WrapAngle1 = " + Str(Total1) + #LFCR$ + "WrapAngle2 = " + Str(Total2) + #LFCR$ + "WrapAngle3 = " + Str(Total3) + #LFCR$ + "WrapAngleDeg = " + Str(Total4) + #LFCR$ + "WrapValue = " + Str(Total5),0)
Please correct my english
http://purebasic.developpez.com/
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

Just copied, pasted and runned:
Image

Does someone know about this wierd Comtois PC behaviour?
Can't understand :?
The only thing i can say is that obviously this way is not conclusive for testing speed :roll:
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
User avatar
Comtois
Addict
Addict
Posts: 1431
Joined: Tue Aug 19, 2003 11:36 am
Location: Doubs - France

Post by Comtois »

Psychophanta wrote:The only thing i can say is that obviously this way is not conclusive for testing speed :roll:
please post a good test , i will try it :)
Please correct my english
http://purebasic.developpez.com/
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

Theorycally your test is good. But in the practice there is conclusive that must be something wrong, because these functions are ASM optimized.
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

There will be a difference between AMD and Intel processors for this kind of optimized approach.
Last edited by netmaestro on Tue Feb 21, 2006 11:21 pm, edited 2 times in total.
BERESHEIT
User avatar
Comtois
Addict
Addict
Posts: 1431
Joined: Tue Aug 19, 2003 11:36 am
Location: Doubs - France

Post by Comtois »

i asked on french forum , it is faster for AMD user .
I have a Pentium 4 2,66.

http://purebasic.hmt-forum.com/viewtopi ... 1540#41540
Please correct my english
http://purebasic.developpez.com/
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

I have AMD Athlon 64 3000+
However, good news to know PB is so fast :wink:

EDIT: 468 344 406 375 688 was the result in a pentium centrino 1.8GHz

NOTE: Since Pentium4 seem to have a speed problem with fprem1 ASM instruction i replaced it to fprem only in the unsigned result functions bcoz it works the same, but signed functions must have fprem1.
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
dagcrack
Addict
Addict
Posts: 1868
Joined: Sun Mar 07, 2004 8:47 am
Location: Argentina
Contact:

Post by dagcrack »

Another AMD buddie! yay! :D

This optimization guide is not bad, but theres others out there:
http://www.website.masmforum.com/mark/index.htm
I really can't stand Intel processors (more likely P4) But oh well, got to support them to produce results as fast as possible... Although people keeps on arguing with me.. "p4 is better".. They just have a bigger cache because their processor is bigger bloated!. You don't gain much with a processor that is so bloated...
! Black holes are where God divided by zero !
My little blog!
(Not for the faint hearted!)
Post Reply