mikrocontroller.net

Forum: FPGA, VHDL & Co. Festkomma Berechnungen


Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,
ich habe folgende Aufgabenstellung:

ich bekomme von einem 16 Bit ADC logischerweise Werte im Bereich von 
0...65535  an einen FPGA geliefert(auf diesem ist ein NIOS Core 
implementiert), diese Werte will ich nun über einen Algorithmus laufen 
lassen, welcher mir aus den erhaltenen Werten bestimmte Dinge berechnen 
soll. Der Algorithmus beinhaltet Additionen, Substaktionen, 
Multiplikationen und Divisionen.
Ich habe das Ganze zunächst in Floating Point Arithmetik implementiert, 
damit funktioniert das Ganze sehr gut.
Nun will ich allerdings nur noch Fixed Point Datentypen verwenden (ist 
eine Vorgabe) und genau hier stoße ich auf Probleme. Wie macht man so 
etwas am besten? Additionen und Substraktionen müssten ja identisch sein 
wie bei floating point, oder? Wie führe ich Divisionen durch?
Beispielsweise will ich den aktuellen ADC Wert durch 65536 dividieren 
und dann mit der Referenzspannung multiplizieren, um den ursprünglichen 
Wert zu erhalten (vor der AD Wandlung). Diesen Wert will ich als 
Festkommawert speichern, um damit weiterrechnen zu können.
Wie programmiert man so etwas ? (in C)

Ich hoffe ich könnt mir weiterhelfen....

danke schonmal und viel grüße
znil

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> (in C)
Falsches Forum?

> will ich den aktuellen ADC Wert durch 65536 dividieren
Wieviele Bits hat dein ADC, dass du davon 16 gleich mal wegschmeißt?

> Diesen Wert will ich als Festkommawert speichern,
Du mußt erst mal dein Festkommaformat definieren:
Wie breit ist das ganze Wort: 8, 16, 32 Bit?
Wieviele Stellen davon sind vor dem Komma, wieviele danach?
Definier das genauer, und zeig deinen Float-Code.
Dann kann dir umgehend geholfen werden ;-)

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Wieviele Bits hat dein ADC, dass du davon 16 gleich mal wegschmeißt?
mein ADC hat 16 Bit, ich schmeiße aber keine davon weg.

Festkommaformat soll 16 Bit sein. Davon 2 Vorkommastellen, 14 
Nachkommastellen.

Hier mal die Berechnung in floating point, vielleicht hilft das weiter:

// gewandelter AD Wert
ReadData[indize] = rxdata;
uint_spannung[indize] = ((((ReadData[indize] / 65536) * 4) - 2) / 2) ;


Mit "uint_spannung[indize]" will ich anschließend weiter rechnen.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Festkommaformat soll 16 Bit sein. Davon 2 Vorkommastellen, 14
> Nachkommastellen.
Also von 0.000 bis 3.99999? (binär 11.11111111111111)
Oder von 0.000 bis 99.9999? (binär 1100011.111111111)
Signed? Unsigned?

Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
von 0.000 bis 3.99999 signed....nur ist mir nicht klar wie ich das 
implementiere....

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> 0.000 bis 3.99999 signed
Das wäre unsigned, weil alles positiv...
Also eher -4.0 bis 3.9999  oder  -2.0 bis 1.9999?

Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ja sorry mein fehler....natürlich von -2.0 bis 1.9999

Autor: ?? (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Beginn mal mit 32 bit. Da kannste mal 8 stellen vor dem Komma und 24 
nach dem Komma definieren. Das sollte mal reichen.

Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja okey mit 32 Bit hätte ich es auch mal gemacht.
Vom Prinzip her ist mir das schon einigermaßen klar, aber wie definiere 
ich das in C ?
Oder wie mache ich beispielsweise eine Division? 4 / 65536 = 61.035 * 
10^-6.
Muss ich dieses Ergebnis solange mit 10 multiplizieren, bis ich eine 
"einigermaßen" genau Zahl vor dem Komma habe und diese dann mit Komma 
interprtieren?
Multiplikationen und Divisionen macht man ja mit Shiftoperationen, oder?

Danke nochmal für eure Antworten....

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mal angenommen, dein 12 Bit ADC gibt
0x000 für -2V aus und
0xfff für knapp +2V und demnach
0x800 für  0V
Du willst das jetzt in eine 16 Bit signed Binärzahl von -2.0 bis +1.9999 
V umrechnen. Der erste Schritt ist daher, mit dem ADC-Wert eine 
Vorzeichenkorrektur durchzuführen. Das geht einfach, indem das 
höchstwertige Bit invertiert und als Vorzeichenbit interpretiert wird.
+1,999V --> 0xfff  --> 0x7ff
 0V     --> 0x800  --> 0x000
-2V     --> 0x000  --> 0x800
Dann wird das Ganze auf 16 Bit erweitert:
+1,999V --> 0xfff  --> 0x7ff0  = 0111 1111 1111 0000
 0V     --> 0x800  --> 0x0000  = 0000 0000 0000 0000
-2V     --> 0x000  --> 0x8000  = 1000 0000 0000 0000
Und jetzt kannst du dir den Kommapunkt einfach mal irgendwo reindenken. 
Tun wir das z.B. zwischen die 2. und 3. Stelle:
+1,999V --> 0xfff  --> 0x7ff0  = 01.11 1111 1111 0000
 0V     --> 0x800  --> 0x0000  = 00.00 0000 0000 0000
-2V     --> 0x000  --> 0x8000  = 10.00 0000 0000 0000
Und betrachten die Wertigkeiten, die sich daraus ergeben:
Vorzeichen
|
00.00 0000 0000 0000
 | || |'-- 1/16
 | || '--- 1/8
 | |'----- 1/4
 | '------ 1/2
 '-------- 1  Vorkommastelle

Für den Fall 01.11 1111 11111 0000 ergibt sich also
1 + 1/2 + 1/4 + 1/8 + 1/16 +....+ 1/1024 + 1/2048 = 1,99952

Einfacher ist es übrigens, wenn du deine Eingangsspannung nur auf 
Nachkommastellen skaliert und den Wert auf -1,0 ... +0,9999 abbildest.
-2V     = -1.0     = 1.000 0000 0000 0000
+1,999V = +0,9999  = 0.111 1111 1111 0000
Du siehst, es hat sich nur (der gedachte!!!) Dezimalpunkt verschoben.

Aber jetzt ist es leider so, dass du zwar die Zahl in deinem 
Festpunktformat hast, diese Zahl aber immer noch nicht menschlich lesbar 
ist. Wie rechnest du sie jetzt also um?

Du willst z.B. jetzt im Zehnersystem 1 Vorkommastelle und 4 
Nachkommastellen haben und anzeigen. Dann nimmst du deine Zahl, 
multiplizierst sie mit dem Skalenendwert (2 minus -2) = 4 und nochmal 
mit 10 hoch Anzahl der Nachkommastellen, in diesem Beispielfall also 
4*10*10*10*10 = 40000. Und zum Schluss wird das Ganze durch die Anzahl 
der Originalbitbreite (16 Bit) geteilt, und fertig ist die Laube.
short zahl;
long  ausgabewert;
:
zahl = adcwert<<4; // 12-Bit right-aligned ADC einlesen und ausrichten
zahl ^= 0x8000;    // unsigned --> signed
ausgabewert = ((long)zahl * 40000)/65536;

Mit einem ADC-Wert von 0xfff erhältst du dann einen Ausgabewert von 
19995, da entspricht 1 (Vorkomma) und 9995 (Nachkomma). Also gelesen 
1,9995.
Mit dem ADC-Wert von 0x000 ergibt das dann -20000, gelesen -2,0000.

Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
danke für die Antworten....habe zwar ein 16 Bit ADC Wert der als 32 Bit 
Zahl dargestellt werden soll aber das dürfte dann kein Problem sein.
Okey dann hab ich die ADC Werte als Festkommazahlen dargestellt. Mit 
diesen Werten will ich dann weiter rechnen. Was passiert beispielsweise, 
wenn ich die Zahl 19995(sprich 1,9995) mit der Zahl 19995(sprich 1,9995) 
multiplizieren will? Dann entsteht ja eine Zahl, welche nicht mehr 16 
Bit, sondern nun 32 bit groß ist, oder? Wie wird das geregelt, wenn mann 
mehrere Multiplikationen bzw. Divisionen durchführen will? Die Zahl wird 
ja immer größer....

danke schonmal für eure Antworten....

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Dann entsteht ja eine Zahl, welche nicht mehr 16
> Bit, sondern nun 32 bit groß ist, oder?
Wie gesagt: am einfachsten ist es, von -1.0 bis 0.9999 zu rechnen. Denn 
dann bleibt das Ergebnis einer Multiplikation auch zwischen -1.0 und 
0.9999  :-o
Das heißt im Klartext, dass du einfach die unteren 16 Bit (auf Kosten 
der Genauigkeit) wegwirfst und nur mit den oberen 16 Bits 
weiterrechnest...  ;-)

Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo,
ich habe das Ganze nun mit -1,0000 bis 0,9999 gerechnet, bekomme aber 
für einen ADC Wert von 0x0000 nicht -1,0000 (-10000) sondern 
+1,0000(10000). Oder muss ich das Vorzeichen immer entsprechend 
interpretieren?

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> bekomme aber für einen ADC Wert von 0x0000 nicht -1,0000 (-10000)
> sondern +1,0000(10000).
Es kann +1,00 nicht geben, weil gilt:
binär                dez
0111111111111111 = +0,99998
0000000000000001 = +0,00002
0000000000000000 =  0
1111111111111111 = -0,00002
1000000000000000 = -1,0

>  ADC Wert von 0x0000
Wenn bei diesem ADC-Wert einfach das erste Bit invertiert wird, bekommst 
du also aus
0x0000 = 0000000000000000 =  0,0   dann
0x8000 = 1000000000000000 = -1,0.

Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hey,
ja hat sich geklärt, ich habe es nun hinbekommen. War ein Denkfehler von 
mir...;)
Ich stelle nun den ADC Wert als 32 Bit Festkommazahl dar. Bereich 
-1,0000(10000) bis +0,9999(9999).

Danke für die Antworten....werde mich bei weiteren Fragen(die bestimmt 
auftauchen) einfach melden....

Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So jetzt habe ich nochmal eine Frage:

Die 32 Bit Werte die nun zwischen -10000(-1.0000) und +9999(+0.9999) 
liegen multipliziere ich nun miteinander. Beispielsweise:
-10000(-1.0000) * +9999(+0.9999) = -99990000(-0.99990000)
Es entsteht also wieder ein 32 Bit Wert mit 8 Nachkommastellen. Soweit 
so gut...

Nun führe ich 500 solcher Multiplikationen durch, d.h. ich habe 500 32 
Bit Werte die in einem Array stehen. Diese Werte will ich nun alle 
aufaddieren und hier liegt meine Frage...Dieser Additionswert ist ja 
dann so groß, dass er nicht mehr in als 32 Bit Integer Wert 
abgespeichert werden kann, oder? Oder ist es doch möglich diesen 
Additionswert als 32 Bit Integer abzuspeichern?

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
znil schrieb:
> So jetzt habe ich nochmal eine Frage:
>
> Die 32 Bit Werte die nun zwischen -10000(-1.0000) und +9999(+0.9999)
> liegen multipliziere ich nun miteinander. Beispielsweise:
> -10000(-1.0000) * +9999(+0.9999) = -99990000(-0.99990000)
> Es entsteht also wieder ein 32 Bit Wert mit 8 Nachkommastellen. Soweit
> so gut...
>
> Nun führe ich 500 solcher Multiplikationen durch, d.h. ich habe 500 32
> Bit Werte die in einem Array stehen. Diese Werte will ich nun alle
> aufaddieren und hier liegt meine Frage...Dieser Additionswert ist ja
> dann so groß, dass er nicht mehr in als 32 Bit Integer Wert
> abgespeichert werden kann, oder? Oder ist es doch möglich diesen
> Additionswert als 32 Bit Integer abzuspeichern?
Wenn das (korrekte) Ergebnis wieder in den 32 bit Zahlen liegt brauchst 
du nur addieren da es sich um einen Ring handelt.

Ansosnten mußt du natürlich ggf dir überlegen was bei einem 
Über/Unterlauf geshehen soll.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
znil schrieb:
> So jetzt habe ich nochmal eine Frage:
>
> Die 32 Bit Werte die nun zwischen -10000(-1.0000) und +9999(+0.9999)
> liegen multipliziere ich nun miteinander. Beispielsweise:
> -10000(-1.0000) * +9999(+0.9999) = -99990000(-0.99990000)
> Es entsteht also wieder ein 32 Bit Wert mit 8 Nachkommastellen. Soweit
> so gut...

Nein, gar nicht gut.
-10000(-1.0000) * +9999(+0.9999) ergibt erstmal durch die Multiplikation
den Wert -99990000, das stimmt.
Aber -99990000 steht doch für -9999.0000, oder nicht?
Der ist aber noch falsch, er muß noch durch 10000 geteilt werden.
(Entsprechend müsste man vor oder nach einer Division mit 10000
multiplizieren).


Dann kann man auch einige von der Sorte addieren...

> ...

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielleicht erleichtert dir das etwas die Qualen des Alltags: 
Beitrag "Festkommazahlen in Würde"

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ach so, ich hatte nicht mehr auf dem Schirm, daß es um FPGA geht; da
wird dir C++ nicht viel helfen.
Bestenfalls kannst du im Quelltext abkupfern, wie ich es dort gemacht 
hatte.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:
> ach so, ich hatte nicht mehr auf dem Schirm, daß es um FPGA geht; da
> wird dir C++ nicht viel helfen.
Ganz ganz oben steht
>>>>> Wie programmiert man so etwas ? (in C)
Glück gehabt  ;-)

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Oder ist es doch möglich diesen
> Additionswert als 32 Bit Integer abzuspeichern?
500 ==> Du hast zusätzliche 9 Bit, die du irgendwo unterbringen oder 
loswerden mußt.

AM einfachsten skalierst du vorher um genau diese 9 Bits zurück ( 
einfach ein Rechts-Shift).
Oder du mußt bei jeder Addition kontrollieren, ob du in den Überlauf 
gekommen bist, und dann skalieren. Diese Art bringt eine bessere 
Dynamik, weil du die Vorkommastellen in einem Überlaufzähler mitzählst. 
Ob du damit was anfangen kannst steht auf einem anderen Blatt.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lothar Miller schrieb:
> Klaus Wachtler schrieb:
>> ach so, ich hatte nicht mehr auf dem Schirm, daß es um FPGA geht; da
>> wird dir C++ nicht viel helfen.
> Ganz ganz oben steht
>>>>>> Wie programmiert man so etwas ? (in C)
> Glück gehabt  ;-)

Jetzt bin ich tatsächlich verwirrt (kann auch am aktuellen Bier liegen).
Wieso steht das dann in der Rubrik FPGA?
Nicht, daß ich von FPGA Ahnung hätte, aber braucht man da C und C++?

OK, wenn das Ding eine CPU emuliert, macht es wieder Sinn.
Dann stellt sich die Frage an den OP, womit er kompilieren kann...

Wenn da was mit C++ auftaucht, bin ich tatsächlich aus dem Schneider.

Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Um das einmal zu klären:

Das ganze wird auf einem FPGA implementiert. Auf diesem FPGA sitzt ein 
NIOS Softcore (synthetische CPU), sodass man darauf in C programmieren 
kann.
In C will ich diese Festkommaberechnugen auch realisieren...

gruß

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok, und wenn es nicht nur mit C geht, sondern auch mit C++,
dann sollte evtl. Beitrag "Festkommazahlen in Würde"
das Leben erleichtern.
Es wird dabei von C++ nichts genutzt, was prinzipiell Rechenzeit
oder Speicher frisst, sondern templates und inline-Funktionen/-Methoden.
Es sollte nicht langsamer sein als handgeschriebener C-Code, ggf.
sogar schneller als Standard-C (wg. inline).
Vor allem gehe ich davon aus, daß die Programme fehlerärmer, leichter
zu schreiben und insbesondere wesentlich lesbarer werden als mit
manueller Festkommaarithmetik in C.

Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
programmiere in C, werde mir den Beitrag aber trotzdem mal anschauen, 
vielleicht hilft das meinem Verständnis...;)

Bin aber nun doch schon recht weit mit meinen Berechnungen 
vorangeschritten. Führe mit den Werten einige Multiplikationen, 
Divisionen und Additionen durch. Ich hoffe nur, dass ich immer richtig 
skaliert habe. Zumindestens die Ergebnisse scheinen aber zu stimmen....

Im quasi letzten Schritt muss ich nun den cosinus einer 32 Bit Zahl 
bilden.
Beispielsweise:
32 Bit Integerzahl = 2403 (entspricht 0.2403).
Nun will ich den Cosinus von 0.2403 bilden. cos(0.2403) = 0.9712
Leider müsste ich für diesen Fall den cos(2403) bilden, was ja nicht 
möglich ist. Wie realisiert man so etwas? Ist es möglich die berechneten 
Cosinuswerte in einer Lookup Table abzulegen?

Ich hoffe ihr könnt mir nocheinmal helfen....danke

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
klar geht das.
Entweder eine LUT für alle möglichen Werte (was wohl eher zu groß ist),
oder interpolieren. Interpolieren kostet nur mäßig Zeit und ist
schon bei etlichen Dutzend bis wenigen Hundert Stützstellen recht
genau, weil der cos ja einen schönen weichen Verlauf hat.
Das kann ja direkt im verschobenen Bereich passieren, also ohne
skalieren zu müssen.

Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Okey danke das ist ja schonmal gut zu wissen....
nur wie setze ich sowas in C um? das ist mir noch nicht wirklich klar. 
Kann mir da vielleicht jemand das Prinzip erklären? oder gibts es solche 
LUTs schon fertig für cosinus und sinus?

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich würde mir  überlegen, welchen Bereich ich brauche
(0 bis pi/2 z.B. und den Rest später durch Verschieben und
Spiegeln rechnen? Oder gleich weniger, wwenn gar nicht der ganze Bereich 
benötigt wird?).
Dann überlegen, wie eng die Stützstellen der Tabelle liegen sollen,
je nachdem, wie genau es gehen soll und ob man einfach den aktuellen
Wert zur nächsten Stützstelle rundet und in die Tabelle schaut (schnell
und ungenau), oder ob man die zwei benachbarten Stützstellen sucht
und dazwischen linear interpoliert (etwas langsamer und genauer).


Dann ein kleines C-Programm auf dem PC, das zu den Stützstellen
die zugehörigen Werte ausgibt, diese Ausgabe in den Quelltext kopieren,
und den Rest entsprechend drumrumbauen.

Wenn du dich zu Anzahl der Stützstellen, Bereich etc. durchgerungen
hast, kann ich notfalls die LUT oder den Rest bauen bzw. skizzieren,
wie es geht.

Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bereich sollte von 0 bis pi gehen.
Stützstellen sollten 1000 sein, um eine ausreichende Genauigkeit zu 
ereichen. Damit muss zwischen den Werten auch nicht interpoliert werden, 
sondern das Runden zum nächsten Wert genügt.
Mein problem ist, dass ich nicht weiß wie ich dies in C unmsetzen soll. 
Muss ich die 1000 Werte händisch in ein Array speichern und dann bei 
enstprechendem Eingansgwert den Arraywert ausgeben?
Wäre echt super, wenn du mir erläutern könntest wie das 
funtioniert....danke

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zu den Abständen:
Wenn du noch frei bist, was die Skalierung angeht, ist es sinnvoll,
nicht eine runde Zahl im Zehnersystem zu nehmen, sondern im 
Zweierrsystem.
Also z.B. nicht mit 1000 zu skalieren, sondern 1024.
Als Stützweite für die LUT nimmt man dann auch etwas in der Art 2^-n
und findet dann die richtige linke Stützstelle zum Interpolieren
durch Rechtsschieben.

Z.B.:
Ist x der Winkel im Bogenmaß, skaliert mit 2^10 = 1024.
Die int32_t-Zahl x = 2403 wäre dann der Winkel 2403/1024=2.346680
im Bogenmaß.

Weiterhin wird die Tabelle mit einer Stützweite von 1/16 erzeugt,
also für die x-Werte  0.0, 0.0625, 0.125000 etc.:
    int    LUT_cos[] =
    {
        100000000, /* cos(0.000000) */
         99804751, /* cos(0.062500) */
         99219766, /* cos(0.125000) */
         ...
    };
(hier sind die cos-Werte mit 100000000 skaliert, anzupassen
je nachdem, wie du sie brauchst).

Dann kann man den Index der linken Stützstelle zu einemx-Wert
einfach durch >>6 (6=10-4, weil 2^10 ist Skalierung der x-Werte,
2^-4 die Schritteite) erzeugen.
Also der cos des x-Wertes 2.346680 (2403 als skalierte 32b-int)
wird dann interpoliert zwischen dem Wert LUT_cos[37] und LUT_cos[38].
LUT_cos[37] deshalb, weil 2403>>6 = 2403/2^6 = 37 ist.
Kurz: mit x=2403 für 2.346680 interpoliert man zwischen
LUT_cos[x>>6] und LUT_cos[(x>>6)+1].

Hier ein Programm, um die Zahlenwert zu erzeugen:
// Time-stamp: "19.08.09 14:18 gen_LUT_cos.c klaus?wachtler.de"
// erzeugt eine Tabelle von Stützstellen für den cos() für den Bereich
// von 0 bis 2*PI (evtl. ein Stützstelle mehr) mit Stützstellen im
// Abstand von D_X.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define D_X   (1.0/16.0)

int main(void)
{
  FILE *f = fopen( "LUT_cos.c", "w" );

  if( f )
  {
    double     x = 0.0;
    unsigned long index = 0;
    fprintf( f, "  {\n" );
    do
    {
      fprintf( f,
               "    %#20.14g, /* [%lu] cos(%8.6f) */\n",
               cos(x),
               index,
               x
               );
      x += D_X;
      ++index;
    }
    while( x<=2*M_PI );

    fprintf( f, "  };\n" );
    fclose( f ); f = NULL;
  }
  else
  {
    fprintf( stderr, "Fehler beim Oeffnen der Datei\n" );
  }
  return 0;
}

Das erzeugt eine Datei der Art:
  {
         1.0000000000000, /* [0] cos(0.000000) */
        0.99804751070010, /* [1] cos(0.062500) */
        0.99219766722933, /* [2] cos(0.125000) */
        0.98247331310126, /* [3] cos(0.187500) */
...
        0.97574766585169, /* [97] cos(6.062500) */
        0.98751477129906, /* [98] cos(6.125000) */
        0.99542565269752, /* [99] cos(6.187500) */
        0.99944941822450, /* [100] cos(6.250000) */
  };

Verwendet wird diese Datei z.B. so:
     double LUT_cos[] =
#include "LUT_cos.c"

Und Interpolation mit den Stützstellen wie oben beschrieben.
Hier sind in der Tabelle double-Werte abgelegt; je nach
gewünschtem Festkommaformat der cos-Werte halt entsprechend
zu ändern.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hat sich mit deinem Beitrag überschnitten, vielleicht
beantwortet es trotzdem die Frage?

Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
okey, auf diese Art und Weise müsst ich ja ein zusätzliches Programm 
schreiben, welches mir die Cosinuwerte berechnet. Hier werden ja auch 
Flaoting Point Berechnungen durchgeführt, oder? Da ich eine Hardware 
verwende die nur Fixed Point Berechnungen durchführen kann, ist dies in 
meinem Fall ja gar nicht möglich. Es sei denn ich hab nicht verstanden 
was du meinst.

Meine Idee ist nun folgende:
Ich führe die Cosinusberechnnungen in einer Exceltabelle durch und 
übertrage diese Ergebnisse(korrekt skaliert) per copy & paste in ein 
Array in meinem Programm.
Nun lege ich noch ein zweites Array an(oder das erste als 
mehrdimensionales) in welchem die Eingangswerte meiner Berechnung 
stehen. Ich habe also zwei Arrays, eines mit den Eingangswerten, eines 
mit den Ergebnissen.
Nun vergleiche ich meinen Wert(mit welchem ich eine Cosinus Berechnung 
durchführen will) mit den Eingangswerten und schaue, ob der Wert direkt 
vorliegt bzw. wenn dies nicht der Fall ist, runde ich auf den nächsten 
Wert.
Dann hole ich mir das dazu entsprechende Ergebnis aus dem zweiten Array.

Ist diese Vorgehensweise prinzipiell möglich? Oder sehe ich das Ganze 
falsch?
Dies ist quasi meine letze Berechnung die ich machen muss. Danach muss 
dieser Wert nur noch einmal multipliziert werden und fertig wäre die 
ganze Geschichte.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
znil schrieb:
> okey, auf diese Art und Weise müsst ich ja ein zusätzliches Programm
> schreiben, welches mir die Cosinuwerte berechnet. Hier werden ja auch
> Flaoting Point Berechnungen durchgeführt, oder? Da ich eine Hardware
> verwende die nur Fixed Point Berechnungen durchführen kann, ist dies in
> meinem Fall ja gar nicht möglich. Es sei denn ich hab nicht verstanden
> was du meinst.

Das Programm musst du nicht mehr schreiben, ich habe es dir doch schon 
hingeschrieben.
Ja, es verwendet floating point arithmetic.
Es muß aber ja nicht auf deinem limitierten Zielsystem laufen, sondern
auf irgendeinem PC. Voraussetzung ist nur, daß du auf dem PC einen
C-Compiler hast.
Das kann ein kostenloser sein (Visual C++ oder gcc für Windows, gcc
für Linux).
Notfalls kann ich dir den obigen Quelltext auch anpassen, wenn ich
Stützweite, Ausgabeskalierung etc. weiß und die erzeugten Zahlen
als Datei schicken, wenn du keine Möglichkeit zum Kompilieren hast.




>
> Meine Idee ist nun folgende:
> Ich führe die Cosinusberechnnungen in einer Exceltabelle durch und
> übertrage diese Ergebnisse(korrekt skaliert) per copy & paste in ein
> Array in meinem Programm.
> Nun lege ich noch ein zweites Array an(oder das erste als
> mehrdimensionales) in welchem die Eingangswerte meiner Berechnung
> stehen. Ich habe also zwei Arrays, eines mit den Eingangswerten, eines
> mit den Ergebnissen.
> Nun vergleiche ich meinen Wert(mit welchem ich eine Cosinus Berechnung
> durchführen will) mit den Eingangswerten und schaue, ob der Wert direkt
> vorliegt bzw. wenn dies nicht der Fall ist, runde ich auf den nächsten
> Wert.
> Dann hole ich mir das dazu entsprechende Ergebnis aus dem zweiten Array.
>
> Ist diese Vorgehensweise prinzipiell möglich? Oder sehe ich das Ganze
> falsch?

Wenn ich dich richtig verstanden habe, ist es schon möglich.
Einfacher finde ich es wie oben gezeigt gleich den C-Quelltext für das
eigentliche Programm zu generieren, aber kann jeder machen wie er will.
(Ich weiß aber nicht mal, ob man Excel trauen kann bei den cos-Werten.)

Die Geschichte mit Runden kostet aber dann auf dem Zeielsystem
auch wieder Rechenzeit; mit dem Vorschlag von mir bzgl. sinnvoller
Skalierung und Stützweite kann man da viel Laufzeit sparen
durch einfaches Shiften statt lange zu rechnen.

Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So ich habe es jetzt mal so gemacht, wie ich es oben beschrieben habe. 
Also LUT mir Excel erstellt und die Werte übertragen. Fürs erste sollte 
mir das genügen...
danke nochmal für eure Hilfe....

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [vhdl]VHDL-Code[/vhdl]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.