Forum: PC-Programmierung Sinus bis pi/2 mit Ansi-C berechnen und.


von F. B. (mrchipsy)


Lesenswert?

Hallo zusammen,
ich absolviere zur Zeit ein Praktikum und soll eine Code entwickeln bei 
dem ich einen Sinus bis pi/2 berechne und davon auf die Restwerte in den 
anderen Intervallen schließen soll.
Es soll eine Variable vorkommen mit der man angeben kann, wie häufig der 
Sinus im ersten Intervall abgetastet werden soll.
Die kompletten Werte sollen im Anschluss in einem Array gespeichert 
werden.
Kann mir jemand sagen wie ich vom ersten Intervall auf die restlichen 
Werte kommen kann? Code-Hilfe wäre auch nicht schlecht:)

Vielen Dank schonmal

von Peter II (Gast)


Lesenswert?

F. B. schrieb:
> Kann mir jemand sagen wie ich vom ersten Intervall auf die restlichen
> Werte kommen kann?

hast du dir mal einen sinus angeschaut? Wenn das 1/4 bekannst ist, dann 
ist der rest nur eine Spiegelung.

von MyName (Gast)


Lesenswert?

>Code-Hilfe wäre auch nicht schlecht:)
Selbst ist der Mann ;-)

Du kannst z.B. eine Look-UP-Table anlegen und abhängig von Deiner 
gewünschten Abtastrate interpolieren. Stichwort DDS.

Oder Du benutzt den CORDIC Algorithmus um beliebig genau Deine Werte zu 
berechnen.

Ein grenzstabiles Filter wäre auch möglich.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

MyName schrieb:

> Selbst ist der Mann ;-)
>
> Du kannst z.B. eine Look-UP-Table anlegen und abhängig von Deiner
> gewünschten Abtastrate interpolieren. Stichwort DDS.
>
> Oder Du benutzt den CORDIC Algorithmus um beliebig genau Deine Werte zu
> berechnen.
>
> Ein grenzstabiles Filter wäre auch möglich.

Käse.

So wie ich die Aufgabe verstehe, ist sin in [0,π/2] gegeben, etwa durch 
math.h von ANSI-C, und daraus sollen dann die fehlenden Funktionswerte 
erhalten werden.

von Ingo (Gast)


Lesenswert?

F. B. schrieb:
> Hallo zusammen,
> ich absolviere zur Zeit ein Praktikum und soll eine Code entwickeln bei
> dem ich einen Sinus bis pi/2 berechne und davon auf die Restwerte in den
> anderen Intervallen schließen soll.
> Es soll eine Variable vorkommen mit der man angeben kann, wie häufig der
> Sinus im ersten Intervall abgetastet werden soll.
> Die kompletten Werte sollen im Anschluss in einem Array gespeichert
> werden.
> Kann mir jemand sagen wie ich vom ersten Intervall auf die restlichen
> Werte kommen kann? Code-Hilfe wäre auch nicht schlecht:)
>
> Vielen Dank schonmal

Du sagst es ja schon, DU machst ein Praktikum, also musst du deinen 
Grips auch selber anstrengen. So schlimm es ist ja nicht.


Ingo

von MyName (Gast)


Lesenswert?

Johann L. schrieb:
> Käse.

Aber was für ein Prachtstück ;-)


Johann L. schrieb:
> So wie ich die Aufgabe verstehe, ist sin in [0,π/2] gegeben, etwa durch
> math.h von ANSI-C, und daraus sollen dann die fehlenden Funktionswerte
> erhalten werden.

Dann wäre es ja nur:
1) Tabelle vorwärts durchlaufen
2) Tabelle rückwärts durchlaufen
3) Tabelle vorwärts durchlaufen und *(-1) nehmen
4) Tabelle rückwärts durchlaufen und *(-1) nehmen

von F. B. (mrchipsy)


Lesenswert?

Ich habe es jetzt so gemacht, dass ich zuerst die Werte bis PI/2 
berechnet und in ein Feld abgelegt habe.
Hierfür habe ich in einer Schleife folgende Funktion ausgeführt:

sin_tab_table[n]= (sin(x)/TAB_SIZE)*PIDIV2;

Die restlichen Werten ergeben sich wie zuvor von MyName beschrieben.
Es gibt allerdings das große Problem das die Werte bis PI/2 falsch sind.
Ist meine Gleichung falsch oder liegt es an falschen Datentypen?

von Peter II (Gast)


Lesenswert?

F. B. schrieb:
> Ist meine Gleichung falsch oder liegt es an falschen Datentypen?

wir sehen keine Datentypen.

von F. B. (mrchipsy)


Lesenswert?

sin() ist die Sinus Funktion aus der math.h von c, TAB_SIZE und PIDIV2
sind per #define festgelegt.

von Peter II (Gast)


Lesenswert?

F. B. schrieb:
> sin() ist die Sinus Funktion aus der math.h von c, TAB_SIZE und PIDIV2
> sind per #define festgelegt.

und wie genau sieht das define aus - ja vermutlich ist dort der fehler 
denn:

0.122/4 = 0

0.122/4.0 = 0.0305

von Peter II (Gast)


Lesenswert?

sorry - vergesst mein letzen post, das beispiel ist unsinn

10/4 = 4
10/4.0 = 2.5

von F. B. (mrchipsy)


Lesenswert?

Die defines sind so angelegt:

#define TAB_SIZE 128
#define PI 3.14159265f
#define PIDIV2 PI/2

von trollaufspürer (Gast)


Lesenswert?

Sollte von den Datentypen her passen. sin() ist eh als double definiert, 
also sollte auch die gesamte Berechnung automatisch in diesem Datentyp 
ausgeführt werden.

von Peter II (Gast)


Lesenswert?

aber die formel versteht ich nicht

> sin_tab_table[n]= (sin(x)/TAB_SIZE)*PIDIV2;

wie wird dann n und x gebildet?

zeigt doch mal den completten code.

von F. B. (mrchipsy)


Lesenswert?

void sin_tab_init(sin_tab_struct* s_sin,sin_tab_struct* s_cos)
{
  int n;
  float x=0;

  memset((void*)s_cos,0,sizeof(sin_tab_struct));
  memset((void*)s_sin,0,sizeof(sin_tab_struct));

  for(n=0;n<=TAB_SIZE;n++){
    sin_tab_table[n]= (sin(x)/TAB_SIZE)*PIDIV2;
    x++;
  }
}

Das ist der Teil, der mir eigentlich meine SinWerte bis PI/2 liefern 
soll. Dies soll in der Initialisierung erfolgen.

von Karl H. (kbuchegg)


Lesenswert?

> sin_tab_table[n]= (sin(x)/TAB_SIZE)*PIDIV2;

das ist Unsinn.

Das Ergebnis eines Sinus ist eine Zahl zwischen -1 und +1

x läuft von 0 bis PIDIV2
und nicht das Ergebnis des Sinus wird mit PIDIV2 multipliziert!

von Peter II (Gast)


Lesenswert?

wozu sin und cos?

sin(x) ist ja wohl unsinn wenn x von 0 bis 128 geth. In der Formel sind 
die klammern falsch.

von Karl H. (kbuchegg)


Lesenswert?

F. B. schrieb:
> void sin_tab_init(sin_tab_struct* s_sin,sin_tab_struct* s_cos)
> {
>   int n;
>   float x=0;
>
>   memset((void*)s_cos,0,sizeof(sin_tab_struct));
>   memset((void*)s_sin,0,sizeof(sin_tab_struct));


Kannst du dir sparen.
Du überschreibst ja soewieso das Feld

>   for(n=0;n<=TAB_SIZE;n++){

gewöhn dir das <= an dieser Stelle gleich wieder ab.
Eine deratige Schleife hat immer die Form

    for( .. = 0;  ... < Anzahl_Elemente; ... ++ )

Immer. Deine Schleife erzeugt (wenn TAB_SIZE zb den Wert 5 hat, eine 
Tabelle mit 6 Einträgen)

>     sin_tab_table[n]= (sin(x)/TAB_SIZE)*PIDIV2;

x ist der Winkel.
Der soll von 0 bis PIDIV2 laufen.

Und zwar so, dass

  wenn n 0 ist, dann soll auch x 0 sein
  wenn n gleich TAB_SIZE ist, soll x den Wert PIDIV2 ergeben

Den Hinweis geb ich dir:
Das Ergebnis vom Sinus rechnest du überhaupt nicht mehr um. Das ist 
direkt der Wert, der in die Tabelle geht. Aber das awas du in den Sinus 
hineinstopfst, das musst du (ausgehend vom n) entsprechend anpassen und 
umrechnen.

(Aber keine Sorge: Du brauchst nicht mehr als den berühmten Dreisatz: 10 
Äpfel kosten 5 Euro 80, wieviel kosten 7 Äpfel?)

von F. B. (mrchipsy)


Lesenswert?

Vielen Dank, dein Beitrag hat mir sehr geholfen.
Durch deine Erläuterung ging das ganze sehr fix.
Es hat allerdings ein wenig länger gedauert, bis ich rausgefunden habe, 
dass meine Laufvariable n erst gecastet werden muss.Naja jetzt läuft die 
Berechnung bis PI/2 schonmal richtig.

von Peter II (Gast)


Lesenswert?

das memset kannst du dir auch sparen, du überschreibst eh alle werte.

von F. B. (mrchipsy)


Lesenswert?

Wie würdet Ihr das Programm weiter aufsetzen? Es geht mir nicht darum 
wie ich auf die Werte der letzten 3 Quadranten komme, sondern einfach 
darum wie ich das ganze verpacke.
Sollte ich ein weiteres Feld anlegen in welches ich die bereits 
berechneten+die restliche Werte reinschreibe?

von Peter II (Gast)


Lesenswert?

F. B. schrieb:
> Wie würdet Ihr das Programm weiter aufsetzen? Es geht mir nicht darum
> wie ich auf die Werte der letzten 3 Quadranten komme, sondern einfach
> darum wie ich das ganze verpacke.
> Sollte ich ein weiteres Feld anlegen in welches ich die bereits
> berechneten+die restliche Werte reinschreibe?

das macht nun überhaupt keinen sinn.

schreibe einfach eine funtion

my_sin(x)

und dieser soll für jedes X den sinus liefern, diese verwendet dann dein 
array. (und nicht die orginal sin funktion)

dann das gleich für cos, da nutzt du aber auch die my_sin funktion.

von F. B. (mrchipsy)


Lesenswert?

Ich sollte die Berechnung nochmal mit Bit-Verschiebung durchziehen.
´Daraus entstand:

float sin_tab_mysin(float eps)
{
  unsigned int quadrant, eps1,eps2, idx;
  float ret;

  // quadrant = 00 --> 1. Quadrant
  // quadrant = 01 --> 2. Quadrant
  // quadrant = 10 --> 3. Quadrant
  // quadrant = 11 --> 4. Quadrant
  eps2=(int)(eps+0.5);
  quadrant = (eps2 & 0xC0000000) >> 30;
  eps1 = eps2 & 0x3FFFFFFF;

  if(quadrant & 0x1)
    eps1 = 0xF0000000 - eps1;

  // 0 < eps1 < 0x80000000
  idx = eps1 >> (30 - 7);

  ret = sin_tab_table[idx];

  if(quadrant & 0x10)
    ret = ret*(-1);

  return ret;
}

allerdings bekomme ich die falschen Werte für den 3 und 4 Quadranten.
Hat jemand eine Idee?

von Karl H. (kbuchegg)


Lesenswert?

F. B. schrieb:
> Ich sollte die Berechnung nochmal mit Bit-Verschiebung durchziehen.

Offenbar hat dein Winkel 'eps' (wieso eigenlich eps? Ein Winkel heißt 
Alpha oder Beta, aber nicht eps. eps ist meistens die Abkürzung für 
Epsilon und der Begriff steht meistens für eine Fehlerschranke) nicht 
den üblichen Wertebereich in Radianten.

> ... nochmal mit Bit-Verschiebung durchziehen.
Wozu?
Wenn es eine Möglichkeit gibt, Berechnungen durch Bitmanipulationen zu 
ersetzen UND dies dann auch tatsächlich schneller ist, DANN machen das 
Compiler seit ungefähr 50 Jahren ganz von alleine.
Du gewinnst dadurch nichts. Du schiesst dir damit höchstens ins Knie.


> allerdings bekomme ich die falschen Werte für den 3 und 4 Quadranten.
> Hat jemand eine Idee?

Nimm Werte an, geh deinen Code durch und mach am Papier die gleichen 
Operationen und sieh dir an, was da passiert. So läuft das nun mal. Du 
hast es programmiert - du suchst auch die Bugs bzw. du suchst wo du 
deinen Denkfehler hast.

Und im übrigen ist es ja nicht verboten, in seinen Code mal 
zwischendurch ein paar printf() einzubauen, mit denen man sich 
Zwischenwerte ausgibt um zu sehen, wie weit die Berechnung mit dem 
übereinstimmt, was man sich vorstellt.

von F. B. (mrchipsy)


Lesenswert?

Ich sitze seit heute morgen davor und komme einfach nicht auf eine 
vernünftige Lösung, leider geht es so wie oben dargestellt überhaupt 
nicht richtig.
Hast du en Tipp für mich?

von Karl H. (kbuchegg)


Lesenswert?

F. B. schrieb:
> Ich sitze seit heute morgen davor und komme einfach nicht auf eine
> vernünftige Lösung, leider geht es so wie oben dargestellt überhaupt
> nicht richtig.
> Hast du en Tipp für mich?
1
float sin_tab_mysin(float eps)
2
{
3
  unsigned int quadrant, eps1,eps2, idx;
4
  float ret;
5
6
  // quadrant = 00 --> 1. Quadrant
7
  // quadrant = 01 --> 2. Quadrant
8
  // quadrant = 10 --> 3. Quadrant
9
  // quadrant = 11 --> 4. Quadrant
10
11
printf( "**********************\n" );
12
printf( "eps = %f\n", eps );
13
14
  eps2=(int)(eps+0.5);
15
  quadrant = (eps2 & 0xC0000000) >> 30;
16
  eps1 = eps2 & 0x3FFFFFFF;
17
18
printf( "eps2 = %u, quadrant = %u, eps1 = %u\n", eps2, quadrant, eps1 );
19
20
  if(quadrant & 0x1)
21
    eps1 = 0xF0000000 - eps1;
22
23
  // 0 < eps1 < 0x80000000
24
  idx = eps1 >> (30 - 7);
25
26
printf( "idx = %u\n" );
27
28
  ret = sin_tab_table[idx];
29
30
printf( "aus Tabelle: %f\n", ret );
31
32
  if(quadrant & 0x10)
33
  {
34
printf( "Quadranten Vorzeichen Korrektur!\n" );
35
    ret = ret*(-1);
36
  }
37
38
printf( "Nach Quadrant korrigiert: %f\n", ret );
39
40
  return ret;
41
}


Lass es laufen, schieb deine Testzahlen durch und studiere den 
generierten Output. Welche Zwischenwerte sind falsch? Wie sollten sie 
sein? Wie sind sie statt dessen?

5 Stunden lang den Code anstarren bringt nichts. Du brauchst Fakten! Und 
dazu brauchst die wiederrum konkrete Zahlenwerte im konkreten 
Fehlerfall.


NB:
  if(quadrant & 0x10)

Sicher, dass du da ein HEX 10 benutzen willst? Welche Werte kann deine 
Variable 'quadrant' annehmen?

Ich denke nämlich, was du in Wirklichkeit wolltest ist

   if(quadrant > 1)

aber durch deine 'Ich muss mit Bitoperationen arbeiten'-Manie hast du 
dir ordentlich selber ins Knie geschossen. Du bist nicht der erste.

von W.S. (Gast)


Lesenswert?

F. B. schrieb:
> ich absolviere zur Zeit ein Praktikum und soll eine Code entwickeln bei
> dem ich einen Sinus bis pi/2 berechne und davon auf die Restwerte in den
> anderen Intervallen schließen soll.

Was für Restwerte ?

Offen gesagt, ich verstehe aus deinen Worten nicht, was du da eigentlich 
anstellen willst/sollst.

Wenn es darum geht, den Sinus nicht nur für Argumente 0..90°, sondern 
auch für beliebige andere Winkel zu berechnen, dann würde ich empfehlen, 
ins Schulbuch zu schauen. Wenn es jedoch darum ginge, aus einem 
Argument tatsächlich den Sinus zu berechnen und nicht bloß ne 
Bibliotheksfunktion aufzurufen, dann schau dir mal den Algorithmus nach 
Pedersen an, den hab ich neulich hier schon mal gepostet.

F. B. schrieb:
> Es soll eine Variable vorkommen mit der man angeben kann, wie häufig der
> Sinus im ersten Intervall abgetastet werden soll.

Mein junger Freund,
ich rat Euch drum,
zuerst Collegium Logicum.
Da wird der Geist Euch wohl dressiert,
in spansche Stiefel eingeschnürt,
daß er bedächgen Schritts fortan
hinwandle die Gedankenbahn,
und nicht etwa die Kreuz und Quer
irrlichteliere hin und her.

Irgendwie find ich Mephisto sympathisch.

Im Klartext:
Lerne erstmal klar zu denken und dann klar deine Gedanken zu 
formulieren, sonst wird nix draus. Das gilt sowohl für das Schreiben 
in einem Forum wie diesem als auch noch viel stärker beim Gebrauch einer 
Programmiersprache.

W.S.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.