c - Why this sin cos look up table inaccurate when radian is large? -


i want create sin cos table optimization, using array index 0 uchar_max, 0 radian index 0, pi/2 radian uchar_max/4:

sincos.h

#include <limits.h> #include <math.h> int sini[uchar_max]; int cosi[uchar_max]; #define magnification 256 #define sin(i) sini[i]/magnification #define cos(i) cosi[i]/magnification  void inittable(){     for(int i=0;i<uchar_max;i++){         sini[i]=sinf(i*2*m_pi/uchar_max)*magnification;         cosi[i]=cosf(i*2*m_pi/uchar_max)*magnification;     } } 

the reason of using uchar_max max want make use of unsigned char overflow simulates radian thats varies 0 2*pi : example, if value of radian 2*pi, index of array becomes uchar_max, because overflows, automatically becomes 0 , no mod required (if use 0 360 domain may need calculate index%360 every time). test radian values:

float rad[]={2.0f,4.0f,6.0f,8.0f,10.0f,-2.0f,-4.0f,-6.0f,-8.0f,-10.0f}; 

like following:

#include "sincos.h" #include <stdio.h> int main(){     inittable();     unsigned char radtoindex;     float rad[]={2.0f,4.0f,6.0f,8.0f,10.0f,-2.0f,-4.0f,-6.0f,-8.0f,-10.0f};     int scalar=123;     printf("scalar=%d\n",scalar);     for(int i=0;i<sizeof(rad)/sizeof(float);i++){         radtoindex=rad[i]*uchar_max/2/m_pi;         printf("%d*sin(%f) : %f , %d\n",scalar,rad[i],scalar*sinf(rad[i]),scalar*sin(radtoindex));     }     return 0; } 

i test table 123*sin(radian),found results starts go beyond actual 1 when magnitude of radian increases (when radian 10 or -10):

scalar=123 123*sin(2.000000) : 111.843582 , 111 123*sin(4.000000) : -93.086708 , -92 123*sin(6.000000) : -34.368107 , -35 123*sin(8.000000) : 121.691063 , 122 123*sin(10.000000) : -66.914597 , -61 123*sin(-2.000000) : -111.843582 , -112 123*sin(-4.000000) : 93.086708 , 90 123*sin(-6.000000) : 34.368107 , 38 123*sin(-8.000000) : -121.691063 , -122 123*sin(-10.000000) : 66.914597 , 59 

and test data:

float rad[]={0.01f,0.1f,1.0f,10.0f,100.0f,1000.0f,-0.01f,-0.1f,-1.0f,-10.0f,-100.0f,-1000.0f}; 

output:

scalar=123 123*sin(0.010000) : 1.229980 , 0 123*sin(0.100000) : 12.279510 , 12 123*sin(1.000000) : 103.500931 , 102 123*sin(10.000000) : -66.914597 , -61 123*sin(100.000000) : -62.282974 , -97 123*sin(1000.000000) : 101.706184 , -25 123*sin(-0.010000) : -1.229980 , 0 123*sin(-0.100000) : -12.279510 , -8 123*sin(-1.000000) : -103.500931 , -100 123*sin(-10.000000) : 66.914597 , 59 123*sin(-100.000000) : 62.282974 , 98 123*sin(-1000.000000) : -101.706184 , 22 

the error increase when magnitude increases, quite sure table becomes inaccurate when radian large. in sincos.h there value magnification control accuracy, changed 256 4096, seems no improvement:

scalar=123 123*sin(0.010000) : 1.229980 , 0 123*sin(0.100000) : 12.279510 , 12 123*sin(1.000000) : 103.500931 , 102 123*sin(10.000000) : -66.914597 , -62 123*sin(100.000000) : -62.282974 , -97 123*sin(1000.000000) : 101.706184 , -25 123*sin(-0.010000) : -1.229980 , 0 123*sin(-0.100000) : -12.279510 , -9 123*sin(-1.000000) : -103.500931 , -100 123*sin(-10.000000) : 66.914597 , 59 123*sin(-100.000000) : 62.282974 , 99 123*sin(-1000.000000) : -101.706184 , 22 

why happen? there logical error of table?

[edit]

code experiences problems angle increases past 360 degrees due wrong "modulo" arithmetic in op's following code. product rad[i]*uchar_max/2/m_pi converted (8-bit) unsigned char modulo 256, yet code scaling tables , code uchar_max (255). last point of answer details aspects of this, yet clear tables , code should use 256, not 255.

unsigned char radtoindex; radtoindex=rad[i]*uchar_max/2/m_pi; // wrong scaling radtoindex=rad[i]*(uchar_max+1)/2/m_pi;  // right 

further, note op's code has undefined behavior when radtoindex == uchar_max invalid index int sini[uchar_max];.

using above fix , 3 below fixes: table size 256, round index, round value of sine, use double table creation results in:

123*sin(2.000000) : 111.843584 , 112 123*sin(4.000000) : -93.086707 , -93 123*sin(6.000000) : -34.368106 , -35 123*sin(8.000000) : 121.691064 , 121 123*sin(10.000000) : -66.914597 , -65 123*sin(-2.000000) : -111.843584 , -112 123*sin(-4.000000) : 93.086707 , 93 123*sin(-6.000000) : 34.368106 , 35 123*sin(-8.000000) : -121.691064 , -121 123*sin(-10.000000) : 66.914597 , 65 

code experiencing double rounding or more preciously: double truncation.

radtoindex=rad[i]*uchar_max/2/m_pi; truncates toward 0. index made smaller, not closest.

table creation sini[i]=sinf(i*2*m_pi/uchar_max)*magnification; truncates toward 0. sini[] made smaller, not closest int.

to improve, round nearest round().

sini[i] = (int) roundf(sinf(i*2*m_pi/uchar_max)*magnification); radtoindex= (int) round(rad[i]*uchar_max/2/m_pi); 

as general note, since float typically 24 bit precision , int 31+sign, use double table creation additional improvements.

sini[i] = (int) round(sin(i*2.0*m_pi/uchar_max)*magnification); 

further, recommend using uchar_max + 1 see bam:

off 1.

the index of array becomes uchar_max, because overflows, automatically becomes 0

uchar_max not overflow, uchar_max + 1 overflows , becomes 0. (unsigned char math)

int sini[uchar_max+1]; (int i=0; i<(uchar_max+1); i++) {   // rather `i*2*m_pi/uchar_max`, use    sini[i]=sinf(i*2*m_pi/(uchar_max + 1))*magnification; 

Comments

Popular posts from this blog

c# - Binding a comma separated list to a List<int> in asp.net web api -

Delphi 7 and decode UTF-8 base64 -

html - Is there any way to exclude a single element from the style? (Bootstrap) -