Hallo zusammen,
hab ein kleines Performance Problem für Sinus und Cosinus Werte von -85
Grad bis +85.
Da dachte ich mir, Werte vorab in PHP berechnen (UART und Float zahlen
fehlt mir noch die Funktion, Ergebnis müßte ja das gleich sein).
Hab ich dann so implemantiert
Meine Frage, kann man mit GCC WINAVR (Atmega16) negative Array-Indexe
angeben und Arrays mit float oder doubel Zahlen.
Wenn nein, wie könnte man das anders lösen?
Die negativen Zahlen könnte ich ja z.B. mit einem Index von 0-170
angeben
und mit
1
winkel+=85
2
cos_winkel=lookup_cos[winkel]// 0-170
aufrufen. Nur mit den floatzahlen "conflicting types for 'lookup_cos'"
gemeckert, wieviel stellen müßte ich dann kürzen?
Über Hilfe würde ich mich sehr freuen. Kenn mich insb. mit C Datentypen
nicht wirklich aus.
Danke & Viele Grüße
Bernd
Hi,
Arrays mit float/double sind kein Problem. Wobei bei avr-gcc/WinAVR
float und double ein und dasselbe sind (auch wenn der Standard was
anderes sagt).
Negative Arrayindizes gehen nicht! Die fangen immer bei 0 an. Die Lösung
hast Du ja schon selbst gefunden.
Leider hast Du nur wahllos irgentweilche Teile Deiner Sourcen
zusammenkopiert und hier gepostet. Falls meine Glaskugel noch halbwegs
funktioniert stimmt da was Grundsätzliches nicht. Z.B. fehlende includes
oder fehlende Deklarationen oder die Zuweisungen stehen ausserhalb einer
Funktion oder oder .... Auf jeden Fall solltest Du vielleicht mal ein
gutes Buch zur Programmiersprache C lesen (z.B. K&R, kannst Du hier im
Forum nach suchen). Außerdem hat Dich die dritte Fehlermeldung ersmal
überhaupt nicht zu interessieren (wahrscheinlich eh nur ein
Folgegfehler), kümmer Dich erstmal um die erste!
CU
warum berechnest du sinus und cosinus? wenn du "stützstellen" einer
funktion im bereich von -90 bis 90 grad berechnest, kannst du mit
"phasenverschiebung" beide werte einfach aus dem array auslesen und
sparst zusätzlich noch 160*sizeof(float) bytes speicherplatz
>sin_cos.c:2: error: conflicting types for 'lookup_cos'>test.c:16: error: previous declaration of 'lookup_cos' was here
Du hast (vermutlich ungewollt) lookup_cos zweimal deklariert, und zwar
richtig als float in test.c, aber zunächst nicht in sin_cos. "gar nicht"
ergibt dann den default-Typ int.
Das das aber schon vom Compiler bemerkt wird, lässt vermuten, daß du da
ein .c-File in ein anderes includierst, oder sonstwie die Struktur
fehlerhaft ist.
Richtig wäre z.B folgende Struktur:
sin_cos.h:
1
#ifndef SIN_COS_H
2
#define SIN_COS_H
3
floatlookup_cos[170];// SIN COS
4
floatlookup_sin[170];
5
#endif // SINCOS_H
sin_cos.c:
1
#include sin_cos.h
2
...
3
lookup_cos[0]=-0.984376643394;
4
...
5
lookup_sin[0]=0.176075619949;
test.c
1
#include sin_cos.h
Das du eine der beiden Tabellen weglassen kannst, wurde ja schon gesagt.
Eigentlich reicht sogar eine Viertelperiode aus, der Rest ergibt sich
aus Phasenverschiebungen.
Falls das alles auf einem AVR laufen soll, müssen die Tabellen mit
PROGMEM ins FLASH. SRAM ist kanpp.
Oliver
Am Rande: Brauchst du diese 'Genauigkeit' tatsächlich? Sonst
multiplizier den Kram mit 2^irgendwas und nimm Festkommazahlen, das ist
aufm AVR meistens schneller.
>Ich hoffe ganz stark, dass du beim Tippen das extern vergessen hast --
aaaarghh....
Da fehlt nicht nur das extern...
Telefonieren und tippen gleichzeitig ist nicht immer ratsam :-)
Also, nochmal richtiger:
sin_cos.h:
Und ich hoffe mal ganz stark, dass die Bezeichnung Sinus bzw. Cosinus
für die Arrays nur annähernd dem entspricht, was in den Arrays enthalten
ist. Deine Werte für einen reinen Sinus bzw. Cosinus wären, gelinde
gesagt, falsch.
>Deine Werte für einen reinen Sinus bzw. Cosinus wären, gelinde>gesagt, falsch.
Falsch ist immer relativ...
lookup_cos[-85] = -0.984376643394;
ist in C-Syntax zwar falsch, der Wert im Bogenmaß aber richtig :-)
Oliver
Oliver wrote:
>>Deine Werte für einen reinen Sinus bzw. Cosinus wären, gelinde>>gesagt, falsch.>> Falsch ist immer relativ...>> lookup_cos[-85] = -0.984376643394;>> ist in C-Syntax zwar falsch, der Wert im Bogenmaß aber richtig :-)
LOL
@Bernd
> lookup_cos[-83] = 0.249540117973;
Ich kann dir nur den Rat geben, mal deinen Taschenrechner zu befragen,
ob der Cosinus von -83 Grad tatsächlich 0.24954 beträgt und dann mal
ergründen, warum dein PHP Programm wohl diesen Wert dafür ausgegeben
hat. Ansonsten wird nämlich aus deinem Perfomance Problem ganz schnell
ein 'Mein Programm baut Mist' Problem. Oliver hat das entsprechende
Stichwort schon gegeben.
Das mit negativen Feldindizes geht ganz gut mit einem kleinen Trick:
1
#include<stdlib.h>
2
#include<stddef.h>
3
#include<stdio.h>
4
#include<string.h>
5
6
7
doublelookup_cos_dummy[171]=
8
{
9
0.08715574275,
10
0.10452846327,
11
0.12186934341,
12
0.13917310096,
13
0.15643446504,
14
0.17364817767,
15
0.19080899538,
16
0.20791169082,
17
0.22495105434,
18
0.24192189560,
19
0.25881904510,
20
0.27563735582,
21
0.29237170472,
22
0.30901699437,
23
0.32556815446,
24
0.34202014333,
25
0.35836794955,
26
0.37460659342,
27
0.39073112849,
28
0.40673664308,
29
0.42261826174,
30
0.43837114679,
31
0.45399049974,
32
0.46947156279,
33
0.48480962025,
34
0.50000000000,
35
0.51503807491,
36
0.52991926423,
37
0.54463903502,
38
0.55919290347,
39
0.57357643635,
40
0.58778525229,
41
0.60181502315,
42
0.61566147533,
43
0.62932039105,
44
0.64278760969,
45
0.65605902899,
46
0.66913060636,
47
0.68199836006,
48
0.69465837046,
49
0.70710678119,
50
0.71933980034,
51
0.73135370162,
52
0.74314482548,
53
0.75470958022,
54
0.76604444312,
55
0.77714596146,
56
0.78801075361,
57
0.79863551005,
58
0.80901699437,
59
0.81915204429,
60
0.82903757256,
61
0.83867056795,
62
0.84804809616,
63
0.85716730070,
64
0.86602540378,
65
0.87461970714,
66
0.88294759286,
67
0.89100652419,
68
0.89879404630,
69
0.90630778704,
70
0.91354545764,
71
0.92050485345,
72
0.92718385457,
73
0.93358042650,
74
0.93969262079,
75
0.94551857560,
76
0.95105651630,
77
0.95630475596,
78
0.96126169594,
79
0.96592582629,
80
0.97029572628,
81
0.97437006479,
82
0.97814760073,
83
0.98162718345,
84
0.98480775301,
85
0.98768834060,
86
0.99026806874,
87
0.99254615164,
88
0.99452189537,
89
0.99619469809,
90
0.99756405026,
91
0.99862953475,
92
0.99939082702,
93
0.99984769516,
94
1.00000000000,
95
0.99984769516,
96
0.99939082702,
97
0.99862953475,
98
0.99756405026,
99
0.99619469809,
100
0.99452189537,
101
0.99254615164,
102
0.99026806874,
103
0.98768834060,
104
0.98480775301,
105
0.98162718345,
106
0.97814760073,
107
0.97437006479,
108
0.97029572628,
109
0.96592582629,
110
0.96126169594,
111
0.95630475596,
112
0.95105651630,
113
0.94551857560,
114
0.93969262079,
115
0.93358042650,
116
0.92718385457,
117
0.92050485345,
118
0.91354545764,
119
0.90630778704,
120
0.89879404630,
121
0.89100652419,
122
0.88294759286,
123
0.87461970714,
124
0.86602540378,
125
0.85716730070,
126
0.84804809616,
127
0.83867056795,
128
0.82903757256,
129
0.81915204429,
130
0.80901699437,
131
0.79863551005,
132
0.78801075361,
133
0.77714596146,
134
0.76604444312,
135
0.75470958022,
136
0.74314482548,
137
0.73135370162,
138
0.71933980034,
139
0.70710678119,
140
0.69465837046,
141
0.68199836006,
142
0.66913060636,
143
0.65605902899,
144
0.64278760969,
145
0.62932039105,
146
0.61566147533,
147
0.60181502315,
148
0.58778525229,
149
0.57357643635,
150
0.55919290347,
151
0.54463903502,
152
0.52991926423,
153
0.51503807491,
154
0.50000000000,
155
0.48480962025,
156
0.46947156279,
157
0.45399049974,
158
0.43837114679,
159
0.42261826174,
160
0.40673664308,
161
0.39073112849,
162
0.37460659342,
163
0.35836794955,
164
0.34202014333,
165
0.32556815446,
166
0.30901699437,
167
0.29237170472,
168
0.27563735582,
169
0.25881904510,
170
0.24192189560,
171
0.22495105434,
172
0.20791169082,
173
0.19080899538,
174
0.17364817767,
175
0.15643446504,
176
0.13917310096,
177
0.12186934341,
178
0.10452846327,
179
0.08715574275,
180
};
181
double*lookup_cos=&lookup_cos_dummy[85];
182
183
intmain(intnargs,char**args)
184
{
185
inti;
186
for(i=-85;i<=85;++i)
187
{
188
printf("cos(%3d) = %10.6f\n",i,lookup_cos[i]);
189
}
190
return0;
191
}
Man macht sich ein dummy-Feld, das man eigentlich nicht direkt verwendet
(lookup_cos_dummy).
Es dient nur dazu, die Werte aufzunehmen und die Adresse des mittleren
Elements zu bekommen. Diese wird in lookup_cos gespeichert.
Ab jetzt kann man die Elemente mit lookup_cos[-85] bis lookup_cos[85]
benutzen.
Ausgabe (gekürzt):
1
cos(-85) = 0.087156
2
cos(-84) = 0.104528
3
cos(-83) = 0.121869
4
cos(-82) = 0.139173
5
...
6
cos( -3) = 0.998630
7
cos( -2) = 0.999391
8
cos( -1) = 0.999848
9
cos( 0) = 1.000000
10
cos( 1) = 0.999848
11
cos( 2) = 0.999391
12
cos( 3) = 0.998630
13
...
14
cos( 82) = 0.139173
15
cos( 83) = 0.121869
16
cos( 84) = 0.104528
17
cos( 85) = 0.087156
Daß hier durch die Symmetrie die Werte doppelt abgelegt sind, und man
mit ganzen Zahlen (Festkomma) vielleicht besser fährt, ist davon
unbenommen.
Hallo,
vielen Dank, das Problem ist die Array Deklaration,
Außerhalb der main Schleife darf man Werte nur so
deklarieren
1
doublelookup_cos[171]=
2
{
3
0.08715574275,
4
0.10452846327,
5
0.12186934341,
6
0.13917310096,
7
0.15643446504
8
};
nicht nicht wie ich
1
lookup_sin[82]=0.313228782433;
2
lookup_sin[83]=0.9683644611;
3
lookup_sin[84]=0.733190320073;
4
lookup_sin[85]=-0.176075619949;
... (tja, so ganz fitt in PHP vs. C Syntax bin ich noch nicht)
@ Karl heinz Buchegger
PHP Cos() verlange als Übergabe ein Bogenmaß,
hatte ich vergessen vorab umzurechnen.
Vielen Dank für die tolle Hilfe!
Sollte jetzt klappen.
> lookup_cos[-85] = -0.984376643394;>> ist in C-Syntax zwar falsch, der Wert im Bogenmaß aber richtig :-)
Autsch!
> Außerhalb der main Schleife
Fehlt nur noch die if-Schleife und case-Schleife...
Sind wir nicht alle ein bischen Schleife?
naja, wenn man den Rechner dazu bringt, daß er abstürzt, neu bootet,
von vorne anfängt und wieder abstürzt, bekommt man mit main() auch eine
Schleife hin.
... wrote:
> Wobei bei avr-gcc/WinAVR> float und double ein und dasselbe sind (auch wenn der Standard was> anderes sagt).
Stimmt übrigens nicht ganz: der Standard hat nur (geringfügig)
höhere Minimalforderungen für die Genauigkeit von double, als man
mit einer 32-bit-Gleitkommazahl erreichen kann, aber er verbietet
es natürlich nicht, dass float und double dasselbe sind, genauso
wie long und int ja auch dasselbe sein können.
@Klaus: das ist wahnsinn, ein dummy pointer und den dann in C als array
verwenden. Ja, es funktioniert, aber genau so eine Programmierung birgt
die grössten Risiken und lässt sich später schwer analysieren.
Vor allem wenn es darum geht negative indexe zu verwalten.
Wenn int als 16bit definiert ist und die adressierung 32bit dann gibt es
eine völlig falsche adresse.
dummy_pointer 0x0010
index -1 + 0xFFFF
ergibt 0x0009 wegen arithmetik Überlauf - erwartetes
Ergebniss
dummy pointer (32bit) 0x00000010
index (16bit) -1 + 0xFFFF - je nach compiler gibt es keinen cast
auf 32bit
ergibt 0x00010009 - nicht erwarteter zugriff
JL
natürlich funktioniert das, ich mache das öfters (in C++ sogar
mithilfe einer template-Klasse als wirklich dynamisches Feld ohne
Feldgrenzenüberschreitung und praktisch ohne Fehlermöglichkeit).
Die Sache mit deiner Beispielrechnung kann ich nicht nachvollziehen;
der Compiler erweitert generell bei Berechnungen auf den längeren
Datentyp.
Zeigerarithmetik ist in beide Richtungen vollkommen legal in C,
und es gibt nicht mehr oder weniger Probleme als man bei anderen
Zeigern/Feldern auch hat.
Im Zweifelsfall kann es so durchaus fehlerärmer sein, als bei jedem
Zugriff mit einem Offset hantieren zu müssen, der früher oder
später doch vergessen wird oder das falsche Vorzeichen hat.
JL wrote:
> dummy pointer (32bit) 0x00000010> index (16bit) -1 + 0xFFFF - je nach compiler gibt es keinen cast> auf 32bit
Wenn er das nicht nach 32 bit promotet, ist er kaputt.
> ergibt 0x00010009 - nicht erwarteter zugriff
Nein, ein Array- oder Zeigerindex in C ist immer vom Typ `int',
damit ist der resultierende Index wieder 0x0009.
JL wrote:
> @Klaus: das ist wahnsinn, ein dummy pointer und den dann in C als array> verwenden. Ja, es funktioniert, aber genau so eine Programmierung birgt> die grössten Risiken und lässt sich später schwer analysieren.
Ist Ansichtssache
> Vor allem wenn es darum geht negative indexe zu verwalten.> Wenn int als 16bit definiert ist und die adressierung 32bit dann gibt es> eine völlig falsche adresse.
Du verwechselst da etwas.
In C ist Array Indizierung und Pointer Arithmetik so definiert, dass man
von der Adresse jedes Array Elements ausgehend, jedes andere Array
Element erreichen kann. Es ist auch definiert, dass die Bildung eines
Pointers der ein Element hinter das Array Ende zeigt legal ist. Was
hingegen nicht definiert ist, ist das Bilden eines Pointers auf ein
Array Element vor den Beginn des Arrays.
Streng gesehen ist das also ok und darf keine Probleme geben:
int arr[5];
int * pPtr = &Arr[5];
Was hingegen nicht definiert ist und wobei sich die Maschine
verschlucken darf (zb indem eine Exception geworfen wird)
in * pPtr2 = &Arr[-1];
weil der resultierende Pointer nicht mehr im Array und auch nicht auf
das Element nach dem Array zeigt.
Aber abgesehen von dieser Spitzfindigkeit (die selten zum tragen kommt,
meist nur dann, wenn eine MMU im Spiel ist) kann innerhalb eines Arrays
jede beliebige Adresse ausgehend von jeder anderen legalen Adresse
gebildet werden. Oder aber der Compiler ist defekt.
Fast einverstanden mit zwei kleinen Änderungen/Ergänzungen:
- Das Bilden von Pointern neben ein Feld ist vollkommen legal,
auch vor dem Feld (nicht nur ein Element dahinter) - zumindest kenne
ich keine gegenteilige Regel und habe es auch noch nie erlebt, daß
es nicht ginge (obwohl ich es regelmäßig probiere).
Illegal ist es, über einen solchen Zeiger dann in den Speicher zu
greifen (egal, ob vor oder hinter das Feld).
- "Jede beliebige Adresse" natürlich nur, soweit es der Größe des
verwiesenen Datentyps entspricht; mit int* beispielsweise kann
man von einer Adresse ausgehend nur jede 2. oder 4. Adresse bilden,
je nach Größe von int. Jede Adresse bilden kann man nur mit char*.
Klaus Wachtler wrote:
> Fast einverstanden mit zwei kleinen Änderungen:> - Das Bilden von Pointern vor ein Feld ist vollkommen legal,> auch vor dem Feld (nicht nur ein Element).
Äh nein. Ist es nicht.
Lies den Standard genau.
Es hat wohl mal Maschinen gegeben, bei denen es spezielle
Pointerregister gab. Bereits das Laden eines Pointers in eines dieser
Register konnte einen Trap auslösen, wenn der Pointer illegal war. Daher
gibt der C-Standard nur die Zusicherung, dass man gefahrlos einen
Pointer in das Array + das unmittelbar folgende Element bilden kann.
Alles andere ist undefiniert (auch wenns heutzutage praktisch immer zu
keinerlei Problemen führt)
> Illegal ist es, über einen solchen Zeiger dann in den Speicher zu> greifen (egal, ob vor oder hinter das Feld).
Das sowieso.
> - "Jede beliebige Adresse" natürlich nur, soweit es der Größe des> verwiesenen Datentyps entspricht; mit int* beispielsweise kann> von einer Adresse ausgehend nur jede 2. oder 4. Adresse bilden,> je nach Größe von int. Jede Adresse bilden kann man nur mit char*.
Das ist nun mal das Wesen wie in C der Zusammenhang zwischen
Pointerarithmetik und Array-Indizierung definiert ist.
ok, mag sein.
Aber aus Interesse: Welche Kisten benehmen sich so merkwürdig?
Muß ich auf meine alten Tage noch damit rechnen, darauf C++
zu programmieren? Dann hätte ich ein Problem...
Auch diese Argumentation von Andrey hat etwas für sich
The problem with your code in its nature is similar to the problem with
the following code
unsigned i;
...
while (i >= 0) dest[i] = source[i--];
Note that an unsigned value will never be negative and the loop will
never end.
Essentially the same thing can happen in case of a pointer. If we think
consider pointers (addresses) as arithmetic values, they are unsigned.
Imagine that your 'beg_of_string' pointer points to address 0. How do
you expect you loop to end in this case? How do you expect to represent
a pointer that is less than '0'?