Sinus und Cosinus in einem µC ohne Fließkomma berechnen

Gast #760064
Lesenswert?

Hallo,
da ich für ein Problem den Sinus und Cosinus für beliebige Winkel in 
hundertstel Grad berechnen müsste und mir da die Wertetabelle zu groß 
wird (144kB), suche ich nach einer Möglichkeit den Sinus und Cosinus 
"live" zu berechnen. Also wie der Taschenrechner auch.

Kann mir da jemand Tipps oder Rechenformeln geben?
#760077
Lesenswert?

Tabelle einfach kleiner machen und linear oder kubisch interpolieren. 
Außerdem reicht es eine viertel Periode in der Tabelle zu halten.

Je nach Qualitätsanforderung ist auch eine Parabelannäherung leicht und 
schnell zu realisieren. Vielleicht ist die Taylorreihenentwicklung der 
Sin/cos-Funktion eine Option.

Ansonsten halt CORDIC.
Gast #760084
Lesenswert?

Zwei Ideen:

1. Du brauchst ja nur den Sinus von 0..90° in der Tabelle zu halten, 
alles
andere ergibt sich aus Quadrantenumrechnung und Verschiebungen.

2. Überlege, welche Genauigkeit notwendig ist. Man kann auch mit 
Reihenentwicklung oder wenigen Stützstellen und Interpolation dazwischen
arbeiten.

Ahoi, Martin
Gast #760090
Lesenswert?

"Du brauchst ja nur den Sinus von 0..90° in der Tabelle zu halten,
alles andere ergibt sich aus Quadrantenumrechnung und Verschiebungen"

Da kann man noch weiter sparen, es reicht IMHO 0-45° ;-)
Gast #760121
Lesenswert?

ja an eine C-Code Implementierung der Taylor Reihe habe ich gedacht.
Die Idee in der C Bibliothek nachzusehn ist natürlich nicht schlecht. 
Lokal auf meinem Rechner hab ich mal in den math.c files gesucht und 
nichts gefunden. Aber mal sehn was sich noch finden lässt.
Gast #760410
Lesenswert?

1
//Aufgabe 2
2
#include <stdio.h>
3
#include <stdlib.h>
4
#include <math.h>
5
#define PI 2.0*asin(1.0)
6
#define NEGATIV 1
7
#define POSITIV 0
8

9
//****Funktion um den Sinus mittels Taylorreihe mit n gliedern zu berechnen****
10
double taylorsin(double x,int n){
11
  int i;
12
  double sum;
13
  sum = 1.0;
14
  for(i=n; i >1 ; i--){
15
    sum = 1.0 - x*x*sum/(double)((2*i-2)*(2*i-1)); //numerisch stabil
16
  }
17
  sum *=x;
18
  return (sum);
19
}
20

21

22
//****Funktion mein_sin (für beliebige x aus R den Sinus zu berechnen)****
23
double mein_sin(double x){
24
  char sign=POSITIV;
25
  double erg;
26
  if (x < 0){
27
    sign = NEGATIV;     //Symmetrieausnutzung Vorzeichenwechsel
28
    x *= -1.0;         //Vorzeichen merken
29
  }
30

31
  x=fmod(x,2*PI);
32
                  //jetzt intervall 0 bis 2PI nur pos
33
  if (x > PI){         //Vorzeichenwechsel ist notwendig
34
                //z.B. 3PI/2 (y = -1) PI subtrahieren = PI/2 (y = 1)
35
    x -= PI;
36
    sign ^= 1;
37
  }
38
  if (x > PI/2.0) x = PI - x; //Symmetrieausnutzung
39
  if(x < PI/6.0) erg = taylorsin(x,8); // ist x in Bereich von 0 bis PI/ 6
40
  //einfach taylorsin aus
41
  else if (x < PI/3.0){
42
    x-=PI/6.0; // ist x in Bereich über PI/6 und unter PI/3
43
      // bringe x in Bereich unter PI/6
44
      x = taylorsin(x,8);
45
    erg = 0.5*(x * sqrt(3.0) + sqrt(1.0 - x*x));
46
  }
47
  else{
48
    x-=PI/3.0; //siehe oben
49
    x = taylorsin(x,8);
50
    erg = 0.5*(x + sqrt(3.0) * sqrt(1.0 - x*x));
51
  }
52
  if(sign!=POSITIV){
53
    erg *= -1;
54
  }
55
  return erg;
56
}
Angehängte Dateien:
Gast #760411
Lesenswert?

Bei 0-45° benötigte er dann sin UND cos, es liefe auf denselben Aufwand 
hinaus.

Oder sin(x) = sqrt( 1 - (sin(90°-x))^2) ausnutzen. Aufgrund des Aufwands 
mit der Wurzel aber auch Käse.
#760812
Lesenswert?

Laut
http://www.eng.monash.edu.au/uicee/gjee/vol8no3/Risse.pdf
verwenden Taschenrechner und x86-Prozessoren den CORDIC-Algorithmus, 
auch wenn es in der Literatur immer wieder anders behauptet wird.
"Seit 1980 implementiert der 8087, der mathematische Coprozessor des 
8086, in Hardware eine Reihe von Konstanten, trigonometrischen und 
logarithmischen Funktionen ... Die Indizien sind erdrückend: simple 
Taylor- Approximation allein kann diesen Implementierungen nicht 
zugrunde liegen!"
Admin Persönliche Seite #760909
Lesenswert?

Der Hauptvorteil vom CORDIC ist, dass man keine Multiplikationen 
braucht, deshalb wird er gerne für Hardwareimplementierungen verwendet. 
Wenn man aber sowieso schon einen Multiplizierer im Controller hat ist 
Cordic nicht mehr unbedingt die beste Wahl, ich würde auch mal 
Polynomapproximation mit Horner-Schema ausprobieren. Wenn die 
Geschwindigkeit keine Rolle spielt ist es natürlich das einfachste die 
sin-Funktion aus der Compilerbibliothek zu benutzen.
#761182
Lesenswert?

Hallo,
könnte man das wie folgt machen:

Man legt eine Tabelle für sin und cos wie folgt an:

phi_tab  sin_tab       cos_tab
------------------------------------
PI/4     sin(PI/4)     cos(PI/4)
PI/16    sin(PI/16)    cos(PI/16)
...
PI/16384 sin(PI/16384) cos(PI/16384)

mit den Additionstheoremen für sin und cos:
sin(x+y) = sin(x)cos(y) + cos(x)sin(y)
cos(x+y) = cos(x)cos(y) - sin(x)sin(y)

kann man sich an den Winkel heranrechnen und ist so theoretisch exakt! 
Man kann die Genauigkeit über die Länge der Tabelle bestimmen.

berechne sin(phi) und cos(phi)

Start:

sin = 0
cos = 1
xphi = 0

Schleife i über die Tabelle:
    xphi= xphi + phi_tab(i)
    if xphi <= phi
        temp = sin*cos_tab(i) + cos*sin_tab(i)
        cos  = cos*cos_tab(i) - sin*sin_tab(i)
        sin  = temp
     else
        xphi = xphi - phi_tab(i)
     endif
ende Schleife

Mit einem Trick kann man etwas schneller sein:

Man Skaliert den Winkel auf 90Grad (PI/2). Dann sind die Einsen im 
Bitmuster für den Winkel die Tabelleneiträge, die für die Näherung 
gebraucht werden:

56,25Grad = 45Grad + 11,25Grad = 10100..0

also die Zeilen für PI/4 und PI/16.

Das ganze muss man sich natürlich mal in Fixpunktarithmetik anschauen.

Waldo
Gast #1261279
Lesenswert?

Hallo!

Wollte für meine Frage kein extra Thema aufmachen, und ich glaube es 
passt hier ganz gut rein... Im Großen und Ganzen geht es auch wieder um 
die Berechnung von böden trigonometrischen Funktionen:
In meinem Programm muß ich öffters mit diesen Funktionen rechnen. Und 
aufgrund der kritischen Rechenzeit kann ich das nicht "online" machen. 
Darum habe ich für Sinus und Cosinus jeweils schon Tabellen mit 
stützwerten abgelegt, was dann so aussieht:

for(i = 0; i <= 512; i++)
{
cosinus[i] = ( (int) (128 + 128 * cos((i  2  3.1416) / 512) * 0.99) );
sinus[i] = ( (int) (128 + 128 * sin((i  2  3.1416) / 512) * 0.99) );
}

Im weitern Verlauf werde ich nur mit Festkommazahlen rechnen. Aber das 
soll nicht eure Sorge sein ;) . Ich hab nur grad irgendwie ein Problem 
mit der Formel für den ARCUS-Sinus und ARCUS-Cosinus. Für die will ich 
auch Tabellen anlegen, aber ich komm nicht auf die Formel...

Kann mir die vielleicht jemand unter die Arme greifen?

Danke,
vG,
Jojo
#1261376
Lesenswert?

Jojo schrieb:

> auch Tabellen anlegen, aber ich komm nicht auf die Formel...

Wie wärs mit: die vorhandene Umstellen

Du hast

  CosV = 128 + 128 * cos( Winkel )

  CosV - 128 = 128 * cos( Winkel )

  ( CosV - 128 ) / 128 = cos( Winkel )

  acos( ( CosV - 128 ) / 128 ) = Winkel


(Mit den 0.99 in deiner Formel hast du dir keinen Gefallen getan)
Gast #1261536
Lesenswert?

Hallo nochmal und danke für die rasche Antwort!

Hm, jetzt, wo ich das so sehe hätte ich da auch selbst drauf kommen 
können ;) ... aber trotzdem danke, manchmal hab ich einfach ein Brett 
vor dem Kopf.

Das mit dem Faktor 0,99 hab ich aus einer Formel, die ich im NEtz 
gefunden habe. Aber richtig, ob ich sie benutze oder weglasse macht 
keinen signifikanten Unterschied.
#1261553
Lesenswert?

Jojo schrieb:

> Das mit dem Faktor 0,99 hab ich aus einer Formel, die ich im NEtz
> gefunden habe. Aber richtig, ob ich sie benutze oder weglasse macht
> keinen signifikanten Unterschied.

Doch, das tut es :-)
Aber es löst ein Problem auf eine etwas seltsame Weise :-)
Mal angenommen es wäre nicht da

Der cos eines Winkels kann minimal -1 sein

128 + ( 128 * -1 ) = 0

aber: er kann maximal +1 sein

128 + ( 128 * 1 ) = 256

Und 256 ist nun mal zu gross für ein Byte :-)

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren