GCC Berechnungen auf dem PC abweichend

Gast #3349463
Lesenswert?

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.
Gast #3349470
Lesenswert?

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.
Gast #3349592
Lesenswert?

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?
#3349609
Lesenswert?

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.
Gast #3349613
Lesenswert?

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/
Gast #3349626
Lesenswert?

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
#3349634
Lesenswert?

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.
Gast #3349641
Lesenswert?

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.
Gast #3349654
Lesenswert?

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.
Gast #3349662
Lesenswert?

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
#3349667
Lesenswert?

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.
Persönliche Seite #3349684
Lesenswert?

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.
Gast #3349692
Lesenswert?

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.
Gast #3349815
Lesenswert?

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.
Gast #3349908
Lesenswert?

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.
**********************************************************************

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren