2015-08-31 12 views
5

Chcę stworzyć cos sin patrzeć w górę tabeli optymalizacji za pomocą indeksu tablicy od 0 do UCHAR_MAX tak, 0 radian jest indeks 0, pi/2 radian jest UCHAR_MAX/4:Dlaczego ten grzech wygląda na niedokładny, gdy radian jest duży?

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; 
    } 
} 

powód użycia UCHAR_MAX jako maks. Chcę dobrze wykorzystać niepodpisany nadmiar char, aby symulować radian, który jest różny od 0 do 2*pi: na przykład, jeśli wartość radian wynosi 2*pi, indeks tablicy staje się UCHAR_MAX , ponieważ przelewa się, to automatycznie ly staje się 0 i mod nie jest wymagany (jeśli używam od 0 do 360 jako domeny, może za każdym razem muszę obliczać index%360). Następnie przetestować go z pewnymi wartościami radian:

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

jak następuje:

#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; 
} 

przetestować tabelę z 123*sin(radian), uznał rozpoczyna wyniki wykraczają poza rzeczywistej jednej kiedy wielkość radian wzrasta (gdy radianie wynosi 10 lub -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 

i testy z innymi danymi:

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}; 

wyjściowa:

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 

Wzrost błąd gdy wzrasta wielkość, więc jestem pewien, stół staje się niedokładna kiedy radian jest duża. W sincos.h jest powiększenie wartości do kontrolowania dokładności, zmieniłem go od 256 do 4096, ale nie wydaje się dużo lepszy:

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 

dlaczego to się stało? czy istnieje błąd logiczny tabeli?

+0

Jak ty messure dokładność? – Zich

Odpowiedz

5

[Edytuj]

experiences Code problemy ze wzrostem kąta ostatnich 360 stopni spowodowane niewłaściwym „arytmetyki modulo” w PO w poniższym kodzie. Produkt rad[i]*UCHAR_MAX/2/M_PI jest konwertowany do (8-bitowego) unsigned char, który jest modulo 256, ale kod jest skalowaniem tabel i kodu przez UCHAR_MAX (255). Ostatnim punktem tej odpowiedzi szczegółów aspekty tego, ale jest jasne, że stoły i kod powinien być użyty 256, a nie 255.

unsigned char radToIndex; 
radToIndex=rad[i]*UCHAR_MAX/2/M_PI; // wrong scaling 
radToIndex=rad[i]*(UCHAR_MAX+1)/2/M_PI; // right 

Ponadto, należy pamiętać, kod OP jest niezdefiniowane zachowanie podczas radToIndex == UCHAR_MAX jako że jest niewłaściwy indeks do int sini[UCHAR_MAX];.

Stosując powyższą poprawkę i 3 poniżej Poprawki: wielkość stołu 256, indeks okrągłe, okrągłe wartości sinusa, należy dwukrotnie na wynikach tworzenia tabeli w:

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 jest również doświadcza double rounding lub więcej kosztownie: podwójne obcięcie.

radToIndex=rad[i]*UCHAR_MAX/2/M_PI; obcina w kierunku 0. Indeks jest więc mniejszy, a nie najbliższy.

Tworzenie tabeli sini[i]=sinf(i*2*M_PI/UCHAR_MAX)*MAGNIFICATION; również obcina w kierunku 0. Tak więc sini[] jest mniejszy, nie jest najbliższy int.

Aby poprawić, po prostu zaokrąglij do najbliższego z round().

sini[i] = (int) roundf(sinf(i*2*M_PI/UCHAR_MAX)*MAGNIFICATION); 
radToIndex= (int) round(rad[i]*UCHAR_MAX/2/M_PI); 

Jako ogólna uwaga, ponieważ float jest zazwyczaj 24 bitowa precyzja i int prawdopodobnie 31 + znak, użyj double do tworzenia tabel dodatkowych ulepszeń.

sini[i] = (int) round(sin(i*2.0*M_PI/UCHAR_MAX)*MAGNIFICATION); 

Ponadto, zalecamy użycie UCHAR_MAX + 1 Zobacz BAM:

Off o 1.

indeks tablicy staje UCHAR_MAX, ponieważ przelewa, to automatycznie staje 0

UCHAR_MAX nie jest przepełnienie, UCHAR_MAX + 1 przepełnienia i staje 0. (unsigned char matematyka)

int sini[UCHAR_MAX+1]; 
for (int i=0; i<(UCHAR_MAX+1); i++) { 
    // Rather than `i*2*M_PI/UCHAR_MAX`, use 
    sini[i]=sinf(i*2*M_PI/(UCHAR_MAX + 1))*MAGNIFICATION; 
+0

Użycie 'double' i' sin() 'w tworzeniu tabeli jest użyteczne, gdy' MAGNIFICATION> 1e23'. – chux

0

źródło problemu

Wygląda na to, że dostajesz błędy z zaokrąglenia liczb zmiennoprzecinkowych i przypisywania liczb zmiennoprzecinkowych do a unsigned char.

Następujący program, zaadaptowany z opublikowanego kodu, demonstruje, w jaki sposób indeks zaczyna się odbiegać, nawet po zaokrągleniu liczby zmiennoprzecinkowej.

#include <limits.h> 
#include <math.h> 

int sini[UCHAR_MAX]; 
int cosi[UCHAR_MAX]; 
double angle[UCHAR_MAX]; 


#define MAGNIFICATION 256 
#define SIN(i) sini[i]/MAGNIFICATION 
#define COS(i) cosi[i]/MAGNIFICATION 

void initTable() 
{ 
    double M_PI = 4.0*atan(1.0); 
    for(int i=0;i<UCHAR_MAX;i++) 
    { 
     angle[i] = i*2*M_PI/UCHAR_MAX; 
     sini[i]=sinf(angle[i])*MAGNIFICATION; 
     cosi[i]=cosf(angle[i])*MAGNIFICATION; 
    } 
} 

#include <stdio.h> 

void test3() 
{ 
    int radToIndexInt; 
    unsigned char radToIndexChar; 
    float radTemp; 
    float rad[]={2.0f,4.0f,6.0f,8.0f,10.0f,-2.0f,-4.0f,-6.0f,-8.0f,-10.0f}; 
    double M_PI = 4.0*atan(1.0); 

    for(int i=0;i<sizeof(rad)/sizeof(float);i++) 
    { 
     radTemp = rad[i]*UCHAR_MAX/2/M_PI; 
     radToIndexInt = round(radTemp); 
     radToIndexInt %= UCHAR_MAX; 
     if (radToIndexInt < 0) 
     { 
     radToIndexInt += UCHAR_MAX; 
     } 

     radToIndexChar = round(radTemp); 

     printf("radToIndexInt: %d, radToIndexChar: %d\n", 
      radToIndexInt, radToIndexChar); 

    } 
} 

int main() 
{ 
    initTable(); 

    test3(); 

    return 0; 
} 

Wyjście z powyższego programu:

radToIndexInt: 81, radToIndexChar: 81 
radToIndexInt: 162, radToIndexChar: 162 
radToIndexInt: 244, radToIndexChar: 244 
radToIndexInt: 70, radToIndexChar: 69 
radToIndexInt: 151, radToIndexChar: 150 
radToIndexInt: 174, radToIndexChar: 175 
radToIndexInt: 93, radToIndexChar: 94 
radToIndexInt: 11, radToIndexChar: 12 
radToIndexInt: 185, radToIndexChar: 187 
radToIndexInt: 104, radToIndexChar: 106 

Rozwiązanie

Korzystając

radToIndex=round(radTemp); 
    radToIndex %= UCHAR_MAX; 
    if (radToIndex < 0) 
    { 
    radToIndex += UCHAR_MAX; 
    } 

obliczyć indeks, otrzymuję bardzo bliskie odpowiedzi:

Oto program, po raz kolejny zaadaptowany z opublikowanego kodu, pokazuje, że korzystanie z powyższej logiki działa.

#include <limits.h> 
#include <math.h> 

int sini[UCHAR_MAX]; 
int cosi[UCHAR_MAX]; 
double angle[UCHAR_MAX]; 


#define MAGNIFICATION 256 
#define SIN(i) sini[i]/MAGNIFICATION 
#define COS(i) cosi[i]/MAGNIFICATION 

void initTable() 
{ 
    double M_PI = 4.0*atan(1.0); 
    for(int i=0;i<UCHAR_MAX;i++) 
    { 
     angle[i] = i*2*M_PI/UCHAR_MAX; 
     sini[i]=sinf(angle[i])*MAGNIFICATION; 
     cosi[i]=cosf(angle[i])*MAGNIFICATION; 
    } 
} 

#include <stdio.h> 

void test2() 
{ 
    int radToIndex; 
    float radTemp; 
    int scalar=123; 
    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}; 
    double M_PI = 4.0*atan(1.0); 

    printf("scalar=%d\n",scalar); 
    for(int i=0;i<sizeof(rad)/sizeof(float);i++) 
    { 
     radTemp = rad[i]*UCHAR_MAX/2/M_PI; 
     radToIndex=round(radTemp); 
     radToIndex %= UCHAR_MAX; 
     if (radToIndex < 0) 
     { 
     radToIndex += UCHAR_MAX; 
     } 
     printf("%d*sin(%f) : %f , %d\n", 
      scalar,rad[i],scalar*sinf(rad[i]),scalar*SIN(radToIndex)); 

    } 
} 

void test1() 
{ 
    int radToIndex; 
    float radTemp; 
    int scalar=123; 
    float rad[]={2.0f,4.0f,6.0f,8.0f,10.0f,-2.0f,-4.0f,-6.0f,-8.0f,-10.0f}; 
    double M_PI = 4.0*atan(1.0); 

    printf("scalar=%d\n",scalar); 
    for(int i=0;i<sizeof(rad)/sizeof(float);i++) 
    { 
     radTemp = rad[i]*UCHAR_MAX/2/M_PI; 
     radToIndex=round(radTemp); 
     radToIndex %= UCHAR_MAX; 
     if (radToIndex < 0) 
     { 
     radToIndex += UCHAR_MAX; 
     } 
     printf("%d*sin(%f) : %f , %d\n", 
      scalar,rad[i],scalar*sinf(rad[i]),scalar*SIN(radToIndex)); 

    } 
} 

int main() 
{ 
    initTable(); 

    test1(); 
    test2(); 

    return 0; 
} 

wyjściowa:

scalar=123 
123*sin(2.000000) : 111.843582 , 111 
123*sin(4.000000) : -93.086708 , -92 
123*sin(6.000000) : -34.368107 , -32 
123*sin(8.000000) : 121.691063 , 121 
123*sin(10.000000) : -66.914597 , -67 
123*sin(-2.000000) : -111.843582 , -111 
123*sin(-4.000000) : 93.086708 , 92 
123*sin(-6.000000) : 34.368107 , 32 
123*sin(-8.000000) : -121.691063 , -121 
123*sin(-10.000000) : 66.914597 , 67 
scalar=123 
123*sin(0.010000) : 1.229980 , 0 
123*sin(0.100000) : 12.279510 , 12 
123*sin(1.000000) : 103.500931 , 103 
123*sin(10.000000) : -66.914597 , -67 
123*sin(100.000000) : -62.282974 , -63 
123*sin(1000.000000) : 101.706184 , 102 
123*sin(-0.010000) : -1.229980 , 0 
123*sin(-0.100000) : -12.279510 , -12 
123*sin(-1.000000) : -103.500931 , -103 
123*sin(-10.000000) : 66.914597 , 67 
123*sin(-100.000000) : 62.282974 , 63 
123*sin(-1000.000000) : -101.706184 , -102 
Powiązane problemy