Gute Tag!
Zu folgender Frage hoffe ich hier auf Hilfe.
Stand der Dinge:
Ich habe mir ein eigentlich sehr einfaches aber mit der Zeit doch
gewachsenes Programm zur Steuerung meines Helikopters geschrieben. Die
Befehle werden in Sequenzen abgelegt, welche ein hohes Maß an
Berechnungen mit Gleitkommazahlen benötigen, welche dann einfach auf
einer SD-Card gespeichert werden. Festpunktrechnung ist hier nicht
möglich.
Idee:
Da die Rechenzeit auf dem ja für Gleitkommaberechnung nicht sehr
geeigneten Atmega328 nun doch schon etwas länger dauern, wollte ich
diese einfach auf dem PC berechnen lassen und die fertig berechneten
Daten auf die SD-Card speichern. Verwendet habe ich hierzu den GCC für
Windows.
Problemstellung:
Die Ergebnisse der Berechnungen mit exakt dem selben C++ Code auf dem
PC/GCC unterscheiden sich signifikant von jenen, welche direkt auf dem
Atmel berechnet wurden. Es scheint, als wären die Berechnungen auf dem
PC wesentlich genauer, was aber nicht gewünscht ist, da der Atmel mit
den auf dem PC vorausberechneten Zwischenwerten, auf seine "ungenaue Art
und Weise" weiter rechnen muss.
Letztlich nun meine Frage:
Wie lässt sich auf dem PC die Art und Weise, wie der Atmel die
Berechnungen durchführt, simulieren? So, dass beide Ergebnisse, von PC
und vom Atmel, exakt übereinstimmen? Leider finde ich hierzu nichts in
Google.
Freue mich auf Hilfe und danke im Voraus.
Die Numerik lässt grüßen, würde ich behaupten.
Du rechnest ja einmal auf einer 8 bit Maschine und dann auf einer 32
oder 64 bit Maschine. Wie gravierend sind denn die Unterschiede? Nicht
das es doch an deinem Programm liegt.
Danke, bis hier her!
Und danke für den Test, aber die Abweichung ist ja bereits bewiesen.
Ich rechne nur mit Float, kein Double.
Ich denke, dass es das ist, was Max schreibt 8bit gegen 64bit. Die
Abweichungen sind nicht groß aber leider zu groß.
Gibt es einen Compiler switch den ich übersehen habe, damit der GCC auf
dem PC ebenfalls mit 8bit Logik kompiliert? Kann man man eventuell
WINAVR irgendwie dazu bewegen, ein kleines Programm mit 8bit Logik für
den PC zu kompilieren?
Du mußt auf dem PC alle Rechnungen als float machen, dann sollte das
gleiche rauskommen, wie auf dem MC.
Also auch alle Zwischenergebnisse wieder als volatile float speichern.
Robert schrieb:> Ich denke, dass es das ist, was Max schreibt 8bit gegen 64bit
nein das ist Unsinn. Man kann auch auf einem 8bit System mit 64bit
variablen rechnen. Es muss dort genau das gleiche rauskommen.
Und nur zur Verdeutlichung.
Mit float (4 Byte) hast du so um die 5 bis 6 signifikante Stellen. Mit
double (8 Byte) sind es aber bereits deren 14 bis 15.
signifikante Stellen sind nicht dasselbe wie Nachkommastellen!
signifikante Stellen sind alle Stellen zusammen. Hast du also die Zahl
1234.567 dann ist nach der 2.ten Nachkommastelle Schluss. Du hast 4
Stellen vor dem Komma, d.h um auf 6 signifikante Stellen zu kommen
kannst du noch 2 Nachkommastellen mit dazu nehmen. Alles dahinter (also
die 7) ist mehr oder weniger gelogen. Und je mehr du dann mit den Zahlen
rechnest und sie weiter bearbeitest, desto schlimmer wird es. Kleine
Fehler, die sich aus der nicht Darstellbarkeit der korrekten Zahl
ergeben, summieren sich auf und haben die Tendenz erst mal eher klein zu
bleiben ehe sie dann recht schnell anwachsen. D.h. nach ein paar
Berechnungen hast du keine 5 oder 6 signifikanten Stellen mehr sondern
nur noch 4 bis 5.
Daher auch der große Unterschied zu double (auch wenn das Problem dort
im Grund nicht anders ist). Es macht eben einen UNterschied, ob ich nach
20 Berechnungen nur noch 4 signifikante Stellen (vertrauenswürdige
Stellen) habe, oder deren 14.
Robert schrieb:> Ich denke, dass es das ist, was Max schreibt 8bit gegen 64bit. Die> Abweichungen sind nicht groß aber leider zu groß.
8Bit-Float gibts nich so wirklich... Vermutlich hast du mit "int" und
"short" etc. gerechnet, was am PC eine andere Größe als am AVR hat.
Verwende Typen wie uint16_t aus der <cstdint>.
nicht "Gast" schrieb:> Ein Standard-Test für Gleitkommarechnungen ist: e^pi-pi und dann> das> Ergebnis mit 20 vergleichen.
ehehehe... http://xkcd.com/217/
Robert schrieb:> Ich rechne nur mit Float, kein Double.
Du schon, aber der Prozessor in deinem PC nicht. Der rechnet trotzdem
mit sehr viel mehr Bits, und daher kommen wohl die Unterschiede.
Es gibt die Compileroption -mpc32. Vielleicht hilft das, ich habs aber
nie ausprobiert.
Oliver
nicht "Gast" schrieb:> Ein Standard-Test für Gleitkommarechnungen ist: e^pi-pi und dann das> Ergebnis mit 20 vergleichen.
Was sagt das Ergebnis 19,99909998 aus?
Oliver schrieb:> Du schon, aber der Prozessor in deinem PC nicht.
das spielt keine rolle. Wenn man float will, dann wird die Rechnung auch
in float gemacht, egal wie die CPU intern rechnet.
Peter II schrieb:> das spielt keine rolle. Wenn man float will, dann wird die Rechnung auch> in float gemacht, egal wie die CPU intern rechnet.
Das kommt zum Teil aber auch darauf an, wie die Berechnung
niedergeschrieben wurde. Ob der COmpiler alle Zwischenergebnisse aus der
80 Bit FPU holt und erst mal wieder auf 32 Bit Floats zurechtstutzt, ehe
dann damit weiter gerechnet wird, oder ob er das nicht tut und erst das
Endergebnis aus der FPU von 80 Bit auf 32 Bit reduziert um es dann in
einem float zu speichern.
Karl Heinz Buchegger schrieb:> Das kommt zum Teil aber auch darauf an, wie die Berechnung> niedergeschrieben wurde. Ob der COmpiler alle Zwischenergebnisse aus der> 80 Bit FPU holt und erst mal wieder auf 32 Bit Floats zurechtstutzt, ehe> dann damit weiter gerechnet wird, oder ob er das nicht tut und erst das> Endergebnis aus der FPU von 80 Bit auf 32 Bit reduziert um es dann in> einem float zu speichern.
sicher? Ich hätte vermutet das er immer das Zwischenergebnis auf float
casten muss. Weil sonst je nach cpu etwas anderes rauskommt.
Robert schrieb:> Festpunktrechnung ist hier nicht möglich.
Festpunktrechnung ist immer moeglich, zur Not halt mit wechselnden
Bereichen.
> Es scheint, als wären die Berechnungen auf dem> PC wesentlich genauer, was aber nicht gewünscht ist, da der Atmel mit> den auf dem PC vorausberechneten Zwischenwerten, auf seine "ungenaue Art> und Weise" weiter rechnen muss.
Wenn du ein Problem mit zu genauen Ergebnissen hast, hast du grossen
Murks produziert. Mir entsteht der Eindruck, du hast deine Algorithmen
schon lange nicht mehr im Griff und deswegen schmeisst dich jede
Abweichung aus der Bahn. Aber gut, wenn du den Murks unbedingt brauchst:
Mach die Vorbereitung der SD-Karte halt auf einem zweiten AVR.
Peter II schrieb:> sicher? Ich hätte vermutet das er immer das Zwischenergebnis auf float> casten muss. Weil sonst je nach cpu etwas anderes rauskommt.
Kommt es ja auch, daher das Problem des TO.
Lies dir spaßeshalber mal diese Seite durch:
http://gcc.gnu.org/onlinedocs/gcc/i386-and-x86_002d64-Options.html
und überlege, warum es da ein paar hilfreiche Compileroptionen zu dem
Thema gibt.
Oliver
Peter II schrieb:> sicher? Ich hätte vermutet das er immer das Zwischenergebnis auf float> casten muss. Weil sonst je nach cpu etwas anderes rauskommt.
Ich hab da jetzt ehrlich gesagt schon lange nicht mehr im Standard
nachgesehen. Würde mich aber wundern, wenn sich da etwas grundlegendes
geändert hätte: Da im Standard nicht definiert wird, wie Floating Point
Arithmetik zu funktionieren hat und schon gar nicht welches System zu
benutzen ist, denke ich: ja - das ist so. Da kommt auf jeder FPU etwas
anderes raus, je nachdem wie die FPU tatsächlich arbeitet. Das war zb
mit dem Microsoft Compiler (früher, heute weiß ich es nicht) ein
mögliches Problem, als die FPU's noch nicht sehr verbreitet waren. Je
nachdem, ob man eine FPU hatte oder nicht, kriegte man leicht
unterschiedliche Ergebnisse. Da man aber Floating Point rechnen sowieso
nie ohne ein ausreichend bemessenes Epsilon macht, war das dann so
tragisch auch wieder nicht, ob sich die Ergebnisse in der 14.ten
signifikanten Stelle unterschieden oder nicht. Gerade bei CAD Sachen
sind derartige Dinge unangenehm: Gibt es noch einen schleifenden
Schnitt, oder sind 2 Gerade parallel? Liegt der Punkt in einer Ebene
oder doch schon darüber oder darunter? Sind 2 Punkte tatsächlich
identisch, etc.
Robert schrieb:> Wie lässt sich auf dem PC die Art und Weise, wie der Atmel die> Berechnungen durchführt, simulieren?
Anstatt "Atmel" meinst du wohl "avr-gcc"?
Letzterer verwendet für solche Berechnungen zur Compilezeit MPFR:
"The MPFR library is a C library for multiple-precision
floating-point computations with correct rounding."
http://www.mpfr.org
Falls dein Programm nicht funktioniert, falls Berechnungen zu genau
sind, hast du m.E. ein ganz anderes Problem.
Bei einem PC lässt sich die größere Genauigkeit fast nicht vermeiden -
klingt komisch - ist aber so.
Da es heute praktisch keinen PC ohne FPU mehr gibt, der Compiler dies
auch weiß, wird oft darauf optimiert. D.H. auch wenn die Übergabe in
kürzerem Format erfolgt, so wird intern oft mit 80 Bit gerechnet. Bleibt
ein unnötiges Zwischenergebnis in der FPU, so erfolgen zwei aufeinander
folgende Rechnungen u.U. mit 80 Bit ohne zwischenzeitliche
Rückkonvertierung. Das aber geht nicht ohne eine erhöhte Genauigkeit.
Zunächst vielen Dank, dass Sie alle Sich heute meinen Kopf zerbrochen
und meine Befürchtungen, dass es von der Hardware abhängt, bestätigt
haben.
Ich werde mir die von Johann L. empfohlene MPFR lib ansehen. Wenn ich
richtig verstehe, bildet die lib Fließkommaberechnung in Software ab,
was dann eigentlich zu Plattform unabhängig gleichen Ergebnissen führen
sollte. Ist dies so, wäre meine Frage beantwortet.
Peter II schrieb:> Karl Heinz Buchegger schrieb:>> Das kommt zum Teil aber auch darauf an, wie die Berechnung>> niedergeschrieben wurde. Ob der COmpiler alle Zwischenergebnisse aus der>> 80 Bit FPU holt und erst mal wieder auf 32 Bit Floats zurechtstutzt, ehe>> dann damit weiter gerechnet wird, oder ob er das nicht tut und erst das>> Endergebnis aus der FPU von 80 Bit auf 32 Bit reduziert um es dann in>> einem float zu speichern.>> sicher? Ich hätte vermutet das er immer das Zwischenergebnis auf float> casten muss. Weil sonst je nach cpu etwas anderes rauskommt.
Ja, aber das wäre auch ein großer Performance-Verlust, weil dazu der
Wert nach jeder Teilrechnung erst in den Speicher geschrieben und wieder
gelesen werden muss. Mit entsprechenden GCC-Optionen kann man aber dafür
sorgen, daß das tatsächlich getan wird.
Aus der GCC-Doku:
**********************************************************************
-ffloat-store
Do not store floating-point variables in registers, and inhibit other
options that might change whether a floating-point value is taken from a
register or memory.
This option prevents undesirable excess precision on machines such as
the 68000 where the floating registers (of the 68881) keep more
precision than a double is supposed to have. Similarly for the x86
architecture. For most programs, the excess precision does only good,
but a few programs rely on the precise definition of IEEE floating
point. Use -ffloat-store for such programs, after modifying them to
store all pertinent intermediate computations into variables.
**********************************************************************
Robert schrieb:> Die> Befehle werden in Sequenzen abgelegt, welche ein hohes Maß an> Berechnungen mit Gleitkommazahlen benötigen, [...]
Kannst Du mal ein Beispiel nennen? Ich kann mir beim besten Willen nicht
vorstellen, wo und warum Du Gleitkommazahlen brauchst.