c++ - Signed Char ATAN2 and ATAN approximations -
c++ - Signed Char ATAN2 and ATAN approximations -
basically, i've been trying create 2 approximation functions. in both cases input "x" , "y" components (to deal nasty n/0 , 0/0 conditions), , need signed char output. in atan2's case, should provide range of +/-pi, , in atan's case, range should +/- pi/2.
i spent entire of yesterday trying wrap head around it. after playing around in excel find overall algorithm based on approximation:
x * (pi/4 + 0.273 * (1 - |x|)) * 128/pi // scale factor @ end switch char format
i came next code:
signed char nabssc(signed char x) { if(x > 0) homecoming -x; homecoming x; } signed char signsc(signed char input, signed char ifzero = 0, signed char scalefactor = 1) { if(input > 0) {return scalefactor;} else if(input < 0) {return -scalefactor;} else {return ifzero;} } signed char divisionsc(signed char numerator, signed char denominator) { if(denominator == 0) // error status {return 0;} else {return numerator/denominator;} } //####################################################################################### signed char atan2sc(signed char y, signed char x) { // @todo create clearer : code deduced through trial , error in excel brute force... not best reasoning in world hey ho if((x == y) && (x == 0)) // error status {return 0;} // prepare algorithm selection const signed char x = abs(x); signed char y = abs(y); if(y > 2) {y = (y << 1) + 4;} const signed char alpha1 = 43; const signed char alpha2 = 11; // create selection if(x <= y) // x/y path { const signed char beta = 64; const signed char = divisionsc(x,y); // x/y const signed char = nabssc(a); // -|x/y| const signed char temp = * (alpha1 + alpha2 * a); // (x/y) * (32 + ((0.273 * 128) / pi) * (1 - |x/y|))) // little angle approximation of arctan(x) if(y < 0) // determine quadrant {return -(temp + beta);} else {return -(temp - beta);} } else // y/x path { const signed char = divisionsc(y,x); // y/x const signed char = nabssc(a); // -|y/x| const signed char temp = * (alpha1 + alpha2 * a); // (y/x) * (32 + ((0.273 * 128) / pi) * (1 - |y/x|))) // little angle approximation of arctan(x) if(x < 0) // determine quadrant { y = signsc(y, -127, 127); // sign(y)*127, if undefined: utilize -127 homecoming temp + y; } else {return temp;} } }
much despair, implementation has errors big 180 degrees, , pretty much everywhere in between well. (i compared atan2f library after converting signed char format.)
i got general gist website: http://geekshavefeelings.com/posts/fixed-point-atan2
can tell me i'm going wrong? , how should approach atan variant (which should more precise it's looking on half range) without craziness.
i'm using qt creator 4.8.1 on windows. end platform specific bit of code micro-controller without fpu, , atan functions 1 of primary functions used. such, efficiency reasonable error (+/-2 degrees atan2 , +/-1 grade atan. these guesstimates now, might increment range, however, 90 degrees not acceptable!) aim of game.
thanks in advance , help!
edit: clarify, outputs of atan2 , atan output signed char value, ranges of 2 types different ranges.
atan2 shall have range -128 (-pi) 127 (+pi - pi/128).
atan have range -128 (-pi/2) 127 (+pi/2 - pi/256).
as such output values 2 can considered 2 different info types.
sorry confusion.
edit2: converted implicit int numbers explicitly signed char constants.
an outline follows. below additional information.
the result angle (a binary angle measure) exactly mathematically divides unit circle 8 wedges. assuming -128 127 char
, atan2sc()
result of each octant 33 integers: 0 32 + offset. (0 32, rather 0 31 due rounding.) atan2sc()
, result 0 64. focus on calculating result of 1 primary octant x,y
inputs , 0 64 result. atan2sc()
, atan2sc()
can both utilize helper function at2()
. atan2sc()
, find intermediate angle a
, utilize a = at2(x,y)/2
. atansc()
, utilize a = at2(-128, y)
.
finding integer quotient a = divisionsc(x,y)
, a * (43 + 11 * a)
loses much info in division. need find atan2 approximation equation uses x,y
maybe in form at2 = (a*y*y + b*y)/(c*x*x + d*x)
.
good utilize negative absolute value nabssc()
. negative range of integers meets or exceed positive range. e.g. -128 -1 vs 1 127. utilize negative numbers , 0, when calling at2()
.
[edit]
below code simplified octant selection algorithm. constructed insure negation of x,y
result in schar_min,schar_max
range - assuming 2's complelment. octants phone call iat2()
, here improvements can made improve precision. note: iat2()
partition x==0
prevented x
not 0 @ point. depending on rounding mode , if helper function shared atansc()
dictate details. suggest 2 piece wise linear table wide integer math not available, else a linear (ay+b)/(cx+d)
. may play more.
the weight of precision vs. performance crucial 1 op's code, not pass along plenty me derive optimal answer. i've posted test driver below assesses precision of ever detail of iat2()
op comes with.
3 pitfalls exist. 1) when reply +180 degree, op appears want -128 bam. atan2(-1, 0.0)
comes +pi. sign reversal may issue. note: atan2(-1, -0.0)
--> -pi. ref. 2) when reply less +180 degrees, depending on iat2()
details, integer bam result +128, tends wrap -128. atan2()
result less +pi or +128 bam. border status needs review inop's final code. 3) (x=0,y=0) case needs special handling. octant selection code finds it.
code signed char atansc(signed char x)
, if needs fast, utilize few if()
s , 64 byte look-up table. (assuming 8 bit signed char). same table used in iat2()
.
.
#include <stdio.h> #include <stdlib.h> // -x > -y >= 0, split 0 not possible static signed char iat2(signed char y, signed char x) { // printf("x=%4d y=%4d\n", x, y); fflush(stdout); homecoming ((y*32+(x/2))/x)*2; // 3.39 mxdiff // homecoming ((y*64+(x/2))/x); // 3.65 mxdiff // homecoming (y*64)/x; // 3.88 mxdiff } signed char iatan2sc(signed char y, signed char x) { // determine octant if (y >= 0) { // oct 0,1,2,3 if (x >= 0) { // oct 0,1 if (x > y) { homecoming iat2(-y, -x)/2 + 0*32; } else { if (y == 0) homecoming 0; // (x=0,y=0) homecoming -iat2(-x, -y)/2 + 2*32; } } else { // oct 2,3 // if (-x <= y) { if (x >= -y) { homecoming iat2(x, -y)/2 + 2*32; } else { homecoming -iat2(-y, x)/2 + 4*32; } } } else { // oct 4,5,6,7 if (x < 0) { // oct 4,5 // if (-x > -y) { if (x < y) { homecoming iat2(y, x)/2 + -4*32; } else { homecoming -iat2(x, y)/2 + -2*32; } } else { // oct 6,7 // if (x <= -y) { if (-x >= y) { homecoming iat2(-x, y)/2 + -2*32; } else { homecoming -iat2(y, -x)/2 + -0*32; } } } } #include <math.h> static void test_iatan2sc(signed char y, signed char x) { static int mn=int_max; static int mx=int_min; static double mxdiff = 0; signed char = iatan2sc(y,x); static const double pi = 3.1415926535897932384626433832795; double = atan2(y ? y : -0.0, x) * 256/(2*pi); if (i < mn) { mn = i; printf ("x=%4d,y=%4d --> %4d %f, mn %d mx %d mxdiff %f\n", x,y,i,a,mn,mx,mxdiff); } if (i > mx) { mx = i; printf ("x=%4d,y=%4d --> %4d %f, mn %d mx %d mxdiff %f\n", x,y,i,a,mn,mx,mxdiff); } double diff = fabs(i - a); if (diff > 128) diff = fabs(diff - 256); if (diff > mxdiff) { mxdiff = diff; printf ("x=%4d,y=%4d --> %4d %f, mn %d mx %d mxdiff %f\n", x,y,i,a,mn,mx,mxdiff); } } int main(void) { int x,y; int n = 127; (y = -n-1; y <= n; y++) { (x = -n-1; x <= n; x++) { test_iatan2sc(y,x); } } puts("done"); homecoming 0; }
btw: fun problem.
c++ c atan2
Comments
Post a Comment