2015-08-31 7 views
5

Ich möchte ein sin cos für Optimierungstabelle nachschlagen erstellen, indem ein Array-Index 0-UCHAR_MAX verwenden, so dass 0 Radiant Index 0, pi/2 Radiant ist UCHAR_MAX/4:Warum ist diese sin cos Tabelle falsch, wenn das Radiant groß ist?

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

der Grund der Verwendung von UCHAR_MAX als max ich die guten Verwendung von unsigned char Überlauf machen will den Radianten das ist von 0 bis 2*pi nur variiert simuliert: zum Beispiel, wenn der Wert von Radianten 2*pi ist, wird der Index des Array UCHAR_MAX weil es überläuft, es automatisch ly wird 0 und es ist keine Mod erforderlich (wenn ich 0 bis 360 als Domäne verwende, muss ich jedes Mal index%360 berechnen). Dann teste ich es mit einigen Radiant Werte:

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

wie folgt aus:

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

ich den Tisch testen mit 123*sin(radian), fand gehen die Ergebnisse beginnt über den eigentlichen man, wenn die Größe von Radiant zunimmt (wenn radian beträgt 10 oder -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 

und Prüfung mit einem anderen Datum:

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

Ausgabe:

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 

Der Fehler erhöhen, wenn Größe zunimmt, so bin ich ganz sicher, dass die Tabelle ungenau wird, wenn Radiant groß ist. In sincos.h gibt es einen Wert MAGNIFICATION die Genauigkeit zu steuern, ich habe es 256 bis 4096 geändert, aber es scheint nicht viel Verbesserung:

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 

warum das passieren? Gibt es einen logischen Fehler der Tabelle?

+0

Wie Sie die Genauigkeit messure? – Zich

Antwort

5

[Bearbeiten]

-Code Erfahrungen Probleme wie der Winkel zunimmt letzten 360 Grad durch falsche "Modulo" Arithmetik in folgendem Code des OP. Das Produkt rad[i]*UCHAR_MAX/2/M_PI wird in ein (8-Bit) unsigned char umgewandelt, das ein Modulo 256 ist, aber der Code skaliert Tabellen und den Code durch UCHAR_MAX (255). Der letzte Punkt dieser Antwort Details Aspekte dieser, aber es ist klar, dass Tabellen und Code sollte Weiterverwendung 256, nicht 255.

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

sein, beachten Sie OPs Code Verhalten nicht definiert ist, wenn radToIndex == UCHAR_MAX wie das ist ein ungültiger Index zu int sini[UCHAR_MAX];.

oben fix verwenden und 3 unten fixes: Tischgröße 256, rund Index, um den Wert von Sinus, verwenden Sie doppelte für das Erstellen von Tabellen ergibt:

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 erlebt auch double rounding oder mehr Kostbar: doppelte Kürzung.

radToIndex=rad[i]*UCHAR_MAX/2/M_PI; schneidet in Richtung 0. So wird der Index kleiner, nicht am nächsten.

Die Tabellenerstellung sini[i]=sinf(i*2*M_PI/UCHAR_MAX)*MAGNIFICATION; wird auch in Richtung 0 abgeschnitten. So wird die sini[] kleiner gemacht, nicht am nächsten int.

Um zu verbessern, einfach auf den nächsten mit round() runden.

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

Als allgemeine Anmerkung, da float sind in der Regel 24-Bit-Präzision und int wahrscheinlich 31 + Zeichen, für weitere Verbesserungen double für Tabellenerstellung verwenden.

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

Ferner empfehlen BAM mit UCHAR_MAX + 1 Siehe:

Off von 1.

der Index der Array UCHAR_MAX wird, weil er überläuft, wird es automatisch 0

UCHAR_MAX ist nicht überlaufen, UCHAR_MAX + 1 überläuft und wird zu 0. (unsigned char math)

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

Verwendung von 'double'und' sin() 'in der Tabellenerstellung nützlich, wenn 'MAGNIFICATION> 1e23'. – chux

0

Quelle des Problems

Es sieht aus wie Sie Fehler werden immer von Rundungs ​​von Gleitkommazahlen und Zuweisen von Gleitkommazahlen ein unsigned char.

Das folgende Programm, das von Ihrem geposteten Code angepasst wurde, zeigt, wie der Index zu abweichen beginnt, auch nachdem Sie die Fließkommazahl umrunden.

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

Ausgabe des obigen Programms:

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 

Lösung

Durch die Verwendung von

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

den Index zu berechnen, erhalte ich sehr nahe Antworten:

Hier ist ein Programm, wieder einmal von Ihrem geposteten Code angepasst, demonstriert, dass die Verwendung der obigen Logik funktioniert.

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

Ausgang:

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 
Verwandte Themen