====== Sinus ======
sinus cosinus integer numbers
Das Beispiel ist in gforth codiert worden.
===== Erklärung =====
Um den Sinus eines Winkels mit ganzen Zahlen (integer) darstellen zu können, skaliert man passend zur Anwendung. Benötigt man z.B. vier Stellen des Sinuswertes, wird er mit 10K multipliziert ausgegeben.
In einem Mikrokontroller geht es bei so einer Funktion auch um eine Balance zwischen Speicherplatz und Rechenzeit. So hat es sich eingebürgert Stützwerte für den Sinus aus einer Tabelle zu entnehmen, und lediglich bei Bedarf Zwischenwerte zu berechnen. Als Index in so eine Tabelle eignet sich die Gradzahl besser als das Bogenmaß. Um die Tabelle klein zu halten, werden oft nur die Sinuswerte von 0 - 90 Grad abgelegt - also die erste Viertelwelle. Manchmal werden auch nur 45 Werte in 2-Grad-Schritten abgelegt.
Um nun alle ganzen Zahlen als Winkel verwenden zu können, muss ein gegebener Winkel auf diese ersten 90 Grad zurück geführt werden. Weil der Sinus eine periodische Funktion ist, können alle Gradzahlen durch entsprechende Phasenverschiebung einfach auf die erste Viertelwelle zurück geführt werden. Es muss dabei jedoch eine Korrektur für das Vorzeichen des Sinuswertes erfolgen.
Baut man den Algorithmus um den Tabellenzugriff
''cell * sinustabelle + @''
auf, erhält man folgenden Ablauf. Winkel zwischen 90 und 180 Grad werden in den ersten Quadranten gespiegelt.
''180 swap - ( 90..180Grad --- 0..90Grad ) ''
Winkel zwischen 180 und 360 Grad werden eine Halbwelle tiefer geschoben.
''180 - ( 180..360Grad --- 0..180Grad ) ''
Weil dabei das Vorzeichen wechselt, wird ein flag hinterlegt, mit dem zum Schluß das Vorzeichen angepasst wird.
''true >r ... r> IF negate THEN''
Winkel größer als 360 Grad werden auf die erste Welle abgebildet.
''360 mod''
Und negative Gradzahlen werden in den positiven Bereich umgeklappt. Auch dabei wechselt das Vorzeichen, und ein weiteres Flag wird hinterlegt, mit dem das Vorzeichen umgekehrt werden kann.
''dup 0< >r abs ... r> IF negate THEN''
===== Algorithmus =====
\ Sinus und Cosinus
\ Tabellengestützte Berechnung für ganze Zahlen.
\ Ergibt auf 10K skalierte Werte.
\ Prototypische Lösung mittels:
\ Gforth 0.6.2, Copyright (C) 1995-2003 Free Software Foundation, Inc.
vocabulary sinus sinus definitions decimal
create sinustabelle \ 0...90 Grad, Index in Grad
0000 , 0175 , 0349 , 0523 , 0698 , 0872 ,
1045 , 1219 , 1392 , 1564 , 1736 , 1908 ,
2079 , 2250 , 2419 , 2588 , 2756 , 2924 ,
3090 , 3256 , 3420 , 3584 , 3746 , 3907 ,
4067 , 4226 , 4384 , 4540 , 4695 , 4848 ,
5000 , 5150 , 5299 , 5446 , 5592 , 5736 ,
5878 , 6018 , 6157 , 6293 , 6428 , 6561 ,
6691 , 6820 , 6947 , 7071 , 7193 , 7314 ,
7431 , 7547 , 7660 , 7771 , 7880 , 7986 ,
8090 , 8192 , 8290 , 8387 , 8480 , 8572 ,
8660 , 8746 , 8829 , 8910 , 8988 , 9063 ,
9135 , 9205 , 9272 , 9336 , 9397 , 9455 ,
9511 , 9563 , 9613 , 9659 , 9703 , 9744 ,
9781 , 9816 , 9848 , 9877 , 9903 , 9925 ,
9945 , 9962 , 9976 , 9986 , 9994 , 9998 ,
10000 ,
: sinus@ cell * sinustabelle + @ ;
: sin ( grad -- sinus )
dup 0< >r abs
360 mod
dup 180 > if 180 - true >r else false >r then
dup 90 > if 180 swap - then
sinus@
r> if negate then
r> if negate then ;
: cos 90 + sin ;
( finis)
===== Verifikation =====
Da gforth die Funktion ''fsin'' bietet, kann ein Vergleich der Tabelleneinträge mit dem berechneten Wert durchgeführt werden. Jedem ''sin'' aus der Tabelle wird ein ''fsin'' gegenübergestellt.
''fsin'' nimmt ein Bogenmaß in Fließkomma Darstellung, daher muss die Gradzahl dahin umgerechnet werden.
\ Verifikation
: .sin sin 6 .r space ;
( rad = grad * pi / 180 )
: rad d>f pi f* 180.0e0 f/ ;
: .fsin s>d rad fsin 10e3 f* 14 6 4 f.rdp ;
: test ( von bis -- ) swap DO cr i .sin i .fsin LOOP ;
Als Ergebnis erhalten wir:
Gforth 0.6.2, Copyright (C) 1995-2003 Free Software Foundation, Inc.
Gforth comes with ABSOLUTELY NO WARRANTY; for details type `license'
Type `bye' to exit
0 91 tt
0 0.000000
175 174.524064
349 348.994967
523 523.359562
698 697.564737
872 871.557427
1045 1045.284633
1219 1218.693434
1392 1391.731010
1564 1564.344650
1736 1736.481777
1908 1908.089954
2079 2079.116908
2250 2249.510543
2419 2419.218956
2588 2588.190451
2756 2756.373558
2924 2923.717047
3090 3090.169944
3256 3255.681545
3420 3420.201433
3584 3583.679495
3746 3746.065934
3907 3907.311285
4067 4067.366431
4226 4226.182617
4384 4383.711468
4540 4539.904997
4695 4694.715628
4848 4848.096202
5000 5000.000000
5150 5150.380749
5299 5299.192642
5446 5446.390350
5592 5591.929035
5736 5735.764364
5878 5877.852523
6018 6018.150232
6157 6156.614753
6293 6293.203910
6428 6427.876097
6561 6560.590290
6691 6691.306064
6820 6819.983601
6947 6946.583705
7071 7071.067812
7193 7193.398003
7314 7313.537016
7431 7431.448255
7547 7547.095802
7660 7660.444431
7771 7771.459615
7880 7880.107536
7986 7986.355100
8090 8090.169944
8192 8191.520443
8290 8290.375726
8387 8386.705679
8480 8480.480962
8572 8571.673007
8660 8660.254038
8746 8746.197071
8829 8829.475929
8910 8910.065242
8988 8987.940463
9063 9063.077870
9135 9135.454576
9205 9205.048535
9272 9271.838546
9336 9335.804265
9397 9396.926208
9455 9455.185756
9511 9510.565163
9563 9563.047560
9613 9612.616959
9659 9659.258263
9703 9702.957263
9744 9743.700648
9781 9781.476007
9816 9816.271834
9848 9848.077530
9877 9876.883406
9903 9902.680687
9925 9925.461516
9945 9945.218954
9962 9961.946981
9976 9975.640503
9986 9986.295348
9994 9993.908270
9998 9998.476952
10000 10000.000000 ok
===== Grafische Darstellung =====
{{ words:sinus1.png?400 |sinus 0..90 Grad}}
{{ words:sinus2.png?400 |sinus 90..180 Grad}}
{{ words:sinus3.png?400 |sinus negative Gradzahl, -180..-90}}
{{ words:sinus4.png?400 |sinus negative Gradzahl, -90..0}}
(Die Zeichnungen dürfen frei verwendet werden. mka)
===== Quellen bzw Links =====
* Forth Magazin "Vierte Dimension", [[http://www.forth-ev.de/filemgmt/viewcat.php?cid=2|Heft 3, 1993]]; Si-Nuß (Sinus auf F-PC); Jörg Staben.
* [[http://amforth.sourceforge.net/|amforth-2.8/lib/sinus.frt]]
* [[amforth-sinus|Etwas andere amforth Version]]
----
===== Sinusberechnung als lineare Interpolation =====
Im vorliegenden Fall kann davon ausgegangen werden, dass die Datenbereiche von x und y begrenzt sind. Man kann sich die Aufnahme der Sinusfunktion so vorstellen, dass eine Leiste der Länge L im Quadranten zwischen x- und y-Achse bewegt wird.
In diesem Fall hat x den Wertebereich zwischen 0 (Leiste auf x-Achse) und dem rechten Winkel(Leiste auf y-Achse). y hat dann den Wertebereich zwischen 0 und L.
In diesem Bereich gibt es fünf rationale Wertepaare:
''(0;0),(1/3;1/2),(1/2;0,707);(2/3;0,866),(1;1), was den Werten für 0, 30, 45, 60 und 90 Grad entspricht. ''
Die Idee war es nun, die beiden Werte auf den Zahlenbereich von 32767 zu skalieren. Außerdem wurden noch zwei Stützwerte ergänzt, für 15 und 75 Grad.
variable sinus
5461 , \ Einteilung (= 32767/6 <=> 90° /6 )
0 , 8191 , 16383 , 23166 , 28376 , 31718 , 32767 , \ Werte
: schrittweite sinus 2 + @ ;
: stuetzwert ( wertnr --- wert ) 2 * 4 + sinus + @ ;
:sin (winkel --- wert ) schrittweite /mod
dup rot swap dup
stuetzwert swap 1 + stuetzwert swap -
schrittweite */
swap stuetzwert + ;
Man muß den Winkel entspechend mit "*/" skalieren und anschließend auch das Ergebnis.
Die Funktion bietet sich auch bei Anwendungen an, wo nur Stützwerte bekann sind, z.B. bei der Leuchtenberechnung.