Weil ich nicht weiß, in welches Forum das gehört schreibe ich es mal
hier her.
Folgendes habe ich mit (Linux) GCC, AVR-GCC und arm-none-eabi-gcc
ausprobiert und habe jedesmal dein gleichen Fehler (die Controller waren
ATMega644 und STM32F401 über serielle Schnittstelle angebunden).
Original geht es um Programmierübungen für Auszubildende (2. Lektion die
ich gerade vorbereite) und bin jetzt selbst am "verzweifeln" oder besser
gesagt überrascht.
Die Problemstellung der Übung sollte sein, dass Displays immer mit
Pixelangaben und den Zollangaben der Diagonalen versehen sind und man
gern die Breite und Höhe für bspw. einen Einbau wissen würde.
Daraus sollte nun errechnet werden, wie breit das Display ist. Hieraus
ergeben sich 2 Lösungsansätze, einmal über Trigonometrie und einmal über
den Satz des Pythagoras:
1
x = 128 pixel
2
+-------------+
3
| /|
4
| d / |
5
| / | y = 64 pixel
6
| / |
7
|/ alpha |
8
+-------------+
9
10
d = 0,96 inch
11
12
Mit Taschenrechner ergibt sich folgendes:
13
--------------------------------
14
Variante 1 (Satz des Pythagoras)
15
--------------------------------
16
17
d[pixel]= sqrt( (x^2) + (y^2) = 143,108 [pixel]
18
19
v= 128 / 143,108 = 0,8944
20
21
x[inch] = v * d = 0,8586
22
x[cm] = 2,18
23
24
25
--------------------------------
26
Variante 2 (Trigonometrie)
27
--------------------------------
28
29
atan(alpha)= 64/128 = 26,565
30
31
x[inch]= cos(alpha) * d = 0.8586
32
x[cm] = 2,18
Soooooweit so gut, nun habe ich das vorab auf einem PC umgesetzt und bin
jetzt erstmal höchst erstaunt über folgendes Programm
Woher kommt nun die Abweichung bei der ersten Berechnung über Pythagoras
(2,27).
Ich kann irgendwie nicht glauben, dass bei einfachem Wurzelziehen ein
größerer Rundungsfehler auftritt als bei cos / tan Berechnungen.
Vor allen Dingen bin ich deswegen erstaunt, weil die zweite Berechnung
über Zwischenspeichern in Variablen die Abweichung nicht hat.
Dann habe ich das mit LibreOfficeCalc mal versucht, das Feld B10
entspricht x, B11 entspricht y:
=(B10/SQRT(B10*B10+B11*B11))*2.54
das Ergebnis hier ist 2.2718450651
Wo liegt nun mein Fehler ? (an Rundungsfehler will ich nicht glauben)
Ralph S. schrieb:> printf("\n Pythagoras");> printf("\n Diagonale [cm] : %.2f",dinch*2.54);> printf("\n Breite [cm] : %.2f",(x / sqrt((x*x)+(y*y))) * 2.54 );> printf("\n");
Bei Breite fehlt doch ein *dinch, daher der Fehler.
Ich bin sooooooooooooooooooooooo blöd, und Entschuldigung für die
Störung, ich hätte mir den ganzen Text hier sparen können:
printf("\n Breite [cm] : %.2f",(x / sqrt((x*x)+(y*y))) *
(dinch*2.54) );
Noch so ein kleiner Tipp am Rande:
Man kann zwar für den Atmega (AVR-GCC)
1
doublemyVar=1234.56789;
schreiben, das wird aber immer ein float sein, sprich myVar hätte
lediglich den Wert 1234.567 (wenn ich mich recht entsinne mit 7 Stellen
Genauigkeit bei Float).
M. K. schrieb:> das wird aber immer ein float sein
Um es zu präzisieren:
AVR-GCC verwendet für float und double den gleichen Datentyp; sizeof
float == sizeof double == 4.
Es hat sich bislang niemand die Mühe gemacht,
64-Bit-floatingpoint-Aritmetik auf den AVR zu portieren.
Ralph S. schrieb:> Wo liegt nun mein Fehler ? (an Rundungsfehler will ich nicht glauben)
Du rundes nicht.
Sonder du kürzt letztendlich das Ergebnis auf 2 Nachkommastellen.
Zum runden muss man z.b. den Befehl round() vor der Ausgabe verwenden.
Jan W. schrieb:> Ralph S. schrieb:>> Wo liegt nun mein Fehler ? (an Rundungsfehler will ich nicht glauben)>> Du rundes nicht.
Nein, das ist nicht der Fehler. Der Fehler wurde bereits ein paar
Minuten nach der Fragestellung (kurz nach Mitternacht) erkannt.
M. K. schrieb:> Noch so ein kleiner Tipp am Rande:>> Man kann zwar für den Atmega (AVR-GCC)>>
1
doublemyVar=1234.56789;
>> schreiben, das wird aber immer ein float sein,
No, es ist kein float sondern ein nicht standardkonformer double.
Ersichtlich z.B. daran, dass avr-g++ double als "d" mangled und float
als "f".
Johann L. schrieb:> No, es ist kein float sondern ein nicht standardkonformer double.> Ersichtlich z.B. daran, dass avr-g++ double als "d" mangled und float> als "f".
Dann hat Rufus also unrecht?
Rufus Τ. F. schrieb:> M. K. schrieb:>> das wird aber immer ein float sein>> Um es zu präzisieren:> AVR-GCC verwendet für float und double den gleichen Datentyp; sizeof> float == sizeof double == 4.>> Es hat sich bislang niemand die Mühe gemacht,> 64-Bit-floatingpoint-Aritmetik auf den AVR zu portieren.
Johann L. schrieb:> No, es ist kein float sondern ein nicht standardkonformer double.> Ersichtlich z.B. daran, dass avr-g++ double als "d" mangled und float> als "f".
Einfach mal obiges Beispiel ausprobieren und anzeigen lassen, was bei
rum kommt ;)
Beim AVR-GCC haben float und double zwar die gleiche Größe und die
gleiche interne Darstellung, es sind aber formal trotzdem zwei
verschiedene Datentypen. Entsprechendes gilt bspw. auch für int und
short, die beim AVR-GCC beide vorzeichenbehaftete 16-Bit-Ganzzahlen,
aber trotzdem verschiedene Datentypen sind.
>Beim AVR-GCC haben float und double zwar die gleiche Größe und die>gleiche interne Darstellung, es sind aber formal trotzdem zwei>verschiedene Datentypen. Entsprechendes gilt bspw. auch für int und>short, die beim AVR-GCC beide vorzeichenbehaftete 16-Bit-Ganzzahlen,>aber trotzdem verschiedene Datentypen sind.
Interessant.
Gruß J
Yalu X. schrieb:> Beim AVR-GCC haben float und double zwar die gleiche Größe und die> gleiche interne Darstellung, es sind aber formal trotzdem zwei> verschiedene Datentypen. Entsprechendes gilt bspw. auch für int und> short, die beim AVR-GCC beide vorzeichenbehaftete 16-Bit-Ganzzahlen,> aber trotzdem verschiedene Datentypen sind.
So kenn ich es auch. Es sind zwar unterschiedliche Datentypen, haben
aber die gleiche Größe was unterm Strich bedeutet, dass man damit das
gleiche Ergebniss erhält. Ich wollte nur drauf hinweisen da ja z.B.
Double eigentlich bedeutet, dass das ein Float mit doppelter Genauigkeit
ist, im Falle des AVR-GCC ist dem aber nicht so, da ist ein Double eben
nicht genauer als ein Float.
Schmunzeln muß, ich wollte jetzt keine Fiskussion über Datentypen
anwerfen. Ursprünglich war das eh alles float und ich hatte die
Datenrypen verändert.
Ich hatte einfach nur schlicht einen Programmierfehler den ich vor
Blindheit nicht sah. Erfreulich für mich war, dass der Codeschnippsel
für alle 3 Plattformen ohne Änderung übersetzt wurde....
Ralph S. schrieb:> Hieraus ergeben sich 2 Lösungsansätze, einmal über Trigonometrie und> einmal über den Satz des Pythagoras:
Der Ansatz über "Trigonometrie" ist allerdings deutlich aufwändiger,
sowohl was Rechenzeit als auch Codegröße angeht, und schlechter
konditioniert ist er auch.
Offenbar gilt cos(atan(x)) = 1/(1+x²) so dass die 2. Formel ohne atan
und cos geschrieben werden kann, und die Umrechnungen zwischen Altrgrad
und Radians fällt auch weg.
Zur Berechnung der Hypothenusenlänge gibt's seit C99 übrigens hypot:
https://linux.die.net/man/3/hypot
jo schrieb:> Mal %lf im Formatstring probiert? %f ist normalerweise für float. Ob es> was ausmacht, ist natürlich sehr compiler- und prozessorabhängig...
Nein. %f ist für double. float kann man an printf gar nicht übergeben,
da bei variadischen Parametern float immer erst in double konvertiert
wird.
Ralph S. schrieb:> Ursprünglich war das eh alles float und ich hatte die> Datenrypen verändert.
Warum hast du sie denn verändert, hilft doch beim AVR eh nicht.
M. K. schrieb:> Warum hast du sie denn verändert, hilft doch beim AVR eh nicht.
Weil ich so dämlich war und den logischen Fehler nicht gesehen hatte,
also hab ich geguckt wie es sich mit unterschiedlichen Datentypen
verhält.
Letztlich ist es halt schlicht MEIN Programmierfehler gewesen, mehr
nicht !
noch mal lächeln muss : Verrückt, dass hieraus ein längerer Thread
entsteht als es muss...
Johann L. schrieb:> Der Ansatz über "Trigonometrie" ist allerdings deutlich aufwändiger,> sowohl was Rechenzeit als auch Codegröße angeht, und schlechter> konditioniert ist er auch.
Das ... wäre das Ziel der Lerneinheit gewesen... die ich nun doch nicht
mache, irgendwie ist mir mein Fehler schlicht peinlich, dass ich ein
anderes Beispiel verwenden werde...
Da hier alle nur dusslig am schnell gefundenen Ergebnis
vorbei labern, müsste ich natürlich noch eine platzsparende
ASM-Festkomma-Alternative anzubieten!
Mach ich aber nicht. Ätsch.