Better atan approximations

Flenser

Senior Member
#1
I was looking for a better atan approximation and I have come up with the following three.

They all work in exactly the same way as the X2 builtin atan function:
  • They provide an arctan function for angles between 0 and 45 degrees.
  • The same coding system is used for the input value. The value is 100 x the real atan value (e.g. 0.39 = 39)
  • e.g let b1 = atan 100 (answer b1 = 45)
To judge the accuracy of the approximations I'm comparing them with the spreadsheet funciton =round(atan(A/100)*180/pi()). The best approximation is one that gives the same result as this spreadsheet function for all 101 values of A from 0 to 100.

For measuring the error I'm comparing them with the spreadsheet funciton =atan(A/100)*180/pi(). Because we are rounding or truncating the result to an integer value the best possible absolute error is 0.5 degree and this is the maximum aboslute error for the spreadsheet funciton =round(atan(A/100)*180/pi()).

I used a 20X2 chip for all my tests.


Atan approximation #1
b0=input value
output value=364-b0*b0/580
  • 7.25% faster than the builtin atan function
  • Gives the same result as the spreadsheet function for 97 out of the 101 input values. The builtin atan function gives the same result for 74 out of 101 input values.
  • Maximum abolute error is 0.72 degrees compared to 0.95 degrees for the builtin atan funciton.
  • Only 12 bytes of code.
  • Does not require any variables for intermediate results. If you don' t need to save the input value you can code the function as b0=364-b0*b0/580
  • You can also define this approximation as a macro "#DEFINE atan(reg) 364-reg*reg/580" and then call it in your code in a similar fashion to the builtin function: b1=atan(b0)
Atan approximation #2
This one line implementation of the code is courtesy of Hippy in post #2 and replaces my original two lines of code.
b0=input value
output value=b0+250*b0/251^$FFFF+581*b0+400/979
  • Half as fast as the builtin atan function.
  • Gives the same result as the spreadsheet function for all 101 input values.
  • Maximum absolute error is 0.5 degrees.
  • 25 bytes of code.
  • Does not require any variables for intermediate results. If you don' t need to save the input value you can code the fuction as b0=b0+250*b0/251^$FFFF+581*b0+400/979
  • You can also define this approximation as a macro "#DEFINE atan(reg) reg+250*reg/251^$FFFF+581*reg+400/979" and then call it in your code in a similar fashion to the builtin function: b1=atan(b0)
If you #DEFINE the atan approximation as a macro then the rules for using that macro are the same as for using the builtin atan unary command:
  • It must be the first command on a program line.
  • It may be followed by additional mathematical commands.
let b1 = atan(30) + 5 ' Is valid
let b1 = 5 + atan(30) ' Is not valid. It won't generate a syntax error but it will return the wrong result.

Atan approximation #3
eeprom (0,1,1,2,2,3,3,4,5,5,6,6,7,7,8,9,9,10,10,11,11,12,12,13,13,14,15,15,16,16,17,17,18,18,19,19,20,20,21,21,22,22,23,23,24,24,25,25,26,26,27,27,27,28,28,29,29,30,30,31,31,31,32,32,33,33,33,34,34,35,35,35,36,36,37,37,37,38,38,38,39,39,39,40,40,40,41,41,41,42,42,42,43,43,43,44,44,44,44,45,45)
read b0, b1
  • Four times as fast as the builtin atan function.
  • Gives the same result as the spreadsheet function for all 101 input values.
  • Maximum absolute error is 0.5 degrees.
  • 3 bytes of code and 101 bytes of data.

The code to test the speed toggles a pin on a 20X2 that is running at 4MHz and the pulse width was measured using PULSEIN on an 08M2 that is running at 4MHz. The values for 100 pulses were averaged to get the final result for each test.

atan speed tests.jpg

Post updated 11/04/2015 - Added approximation #3
Post updated 06/04/2015 - Replaced these original two lines of code for approximation #2 with the one line of code provided by Hippy
b3=b0+250*b0/251
output value=580-b3*b0+400/979
 
Last edited:

hippy

Technical Support
Staff member
#2
Interesting work and quite impressive. Your approximation #2 can be refactored to not require an additional temporary variable as follows -

b4 = b0+250*b0/251^$FFFF+581*b0+400/979

That rearranges "a-b" to become "-b+a" and uses an 'invert and add one' to do the two's complement negation of b; "b^$FFFF+1+a". There might even be some scope for further optimisation.
 

Flenser

Senior Member
#3
Your approximation #2 can be refactored to not require an additional temporary variable
Hippy,

Your one line implementation of the code is 1 byte shorter and 6% faster than my original two lines of code, plus it allows for the elegance of #DEFINEing the second approximation as a macro. Sensational!

I've updated my original post to show your version. Thanks for the tip about how to do the two's complement negation.

Flenser
 

BESQUEUT

Senior Member
#4
ATAN approximation #4

Slow, but accuracy better than 0,001° :
Code:
#simspeed 10

symbol X=w9
symbol Y=w10
symbol T1=w11
symbol T2=w12

symbol I=b2

eeprom(0,0,60,2,121,4,182,6,242,8,46,11,105,13,164,15,221,17,22,20,78,22,133,24,186,26,238,28,33,31,82,33,130,35,176,37,219,39,5,42,45,44,83,46,119,48,152,50,183,52,212,54,238,56,5,59,26,61,44,63,59,65,71,67,80,69,86,71,90,73,90,75,86,77,80,79,70,81,57,83,41,85,21,87,254,88,227,90,197,92,163,94,126,96,85,98,41,100,248,101,197,103,141,105,82,107,19,109,209,110,138,112,64,114,243,115,161,117,76,119,243,120,151,122,54,124,210,125,107,127,255,128,144,130,30,132,167,133,45,135,176,136,46,138,169,139,33,141,149,142,5,144,114,145,220,146,66,148,164,149,3,151,95,152,183,153,12,155,94,156,172,157,247,158,63,160,131,161,197,162,3,164,62,165,118,166,170,167,220,168,11,170,54,171,95,172,133,173,168,174,200,175,229,176)


sertxd("----",13,10)
for x=0 to 100 step 10   ' ATAN from 0.0000 to 0.0100
	gosub ATAN1
	sertxd ("ATAN(",#X,")=",#Y,13,10)
next X

sertxd(".....",13,10)
for x=4060 to 4070   ' ATAN from 0.4060 to 0.4070
	gosub ATAN1
	sertxd ("ATAN(",#X,")=",#Y,13,10)
next X
sertxd(".....",13,10)
for x=9980 to 10005   ' ATAN from 0.9980 to 1.0000
	gosub ATAN1
	sertxd ("ATAN(",#X,")=",#Y,13,10)
next X
end

[color=Green]' Y=ATAN(X)   45°=45000  ATN(45°)=10000[/color]
[color=Black]ATAN1:
      [/color][color=Purple]I[/color][color=DarkCyan]=[/color][color=Purple]X[/color][color=DarkCyan]/[/color][color=Navy]100[/color][color=DarkCyan]*[/color][color=Navy]2
      [/color][color=Blue]read [/color][color=Purple]I[/color][color=Black],[/color][color=Blue]WORD [/color][color=Purple]T1
      i[/color][color=DarkCyan]=[/color][color=Purple]i[/color][color=DarkCyan]+[/color][color=Navy]2
      [/color][color=Blue]read [/color][color=Purple]I[/color][color=Black],[/color][color=Blue]WORD [/color][color=Purple]T2
      T2[/color][color=DarkCyan]=[/color][color=Purple]T2[/color][color=DarkCyan]-[/color][color=Purple]T1
      Y[/color][color=DarkCyan]=[/color][color=Purple]X[/color][color=DarkCyan]//[/color][color=Navy]100[/color][color=DarkCyan]*[/color][color=Purple]T2[/color][color=DarkCyan]/[/color][color=Navy]100[/color][color=DarkCyan]+[/color][color=Purple]T1[/color][color=DarkCyan]+[/color][color=Navy]1[/color]
[color=Blue]RETURN[/color]
Accuracy curve :
Tan_Err.jpg
 
Top