Hi Leute, Ich arbeite mich grade in das Programmieren von AVR's mit C ein. Jetzt ist mit aufgefallen das eine Divison von 2 double zahlen, nur auf 5 stellen nach dem komma genau ist. Das reicht ja bei weiten aus, für meine Berechnungen, aber woher kommt denn die Ungenauigkeit? Hab die Berechnung mal mit Virsual C++ gemacht und durch den Debugger geschickt, dort stimmt das Ergebnis noch bei der 12.stelle. mein Quellcode: #include <stdlib.h> #include "lcd.h" double i; char z[15]; int main(void) { i=134.0/7.0; lcd_init(LCD_DISP_ON); lcd_clrscr(); dtostrf(i,14,10,z); lcd_puts(z); } mfg michi
Hi beim AVRGCC sind sowohl float als auch double 32 Bit Fließkommazahlen. Float ist ja schon langsam genug. Double (also 64 Bit) dürfte dann erst recht übel werden. Matthias
Hi, Wenn man präzise Ergebnise braucht, ist eine gewisse abarbeitungsdauer akzeptabel oder? Du meinst also beim AVRGCC gilt nicht long float = double ?! Sondern float = long float = double ??? Float ist das höchste was geht? Oder wie soll ich das verstehen? mfg
Die Division hat nichts mit dem Compiler zu tun, sonder mit dem Präprozessor. Mach es über Variable, dann wird das Ergebnis genauer. SJ Josef
also du meinst double i, x, y; main () { x = 134.0; y = 7.0; i = x / y; ... ... ... }
Das hat auch damit zu tun, daß der µController das binäre Zahlensystem nutzt und der Mensch das Dezimalsystem. Hierzu muß man sich (mal wieder) auch mal Gedanken zu den Grundlagen machen: Wie stellt ein Computer eine Dezimalzahl dar? Die gesamte Theorie zur Darstellung von Dezimalbrüchen im Binärsystem sprengt leider den Rahmen hier. Zum Beispiel ist es unmöglich, die Zahl 0.1 (dezimal) exakt als Binärzahl mit endlichen Nachkommastellen darzustellen. Durch diese Inkompatibilität kommt es bei jeder Berechnung (vornehmlich bei der Division) zu Rundungsfehlern, die sich mit der Anzahl der Rechenoperationen addieren. Im PC-Bereich ist man schon lange gewohnt, mit Double-Float zu arbeiten, aber beim µController ist dies aus Performance/Ressourcen-Gründen nicht sinnvoll und außerdem verlagert es das Problem nur. Auch mit einer Double-Float kann der Wert von 0.1 nur angenähert, aber nicht exakt binär dargestellt werden. Die einzige Möglichkeit, eine exakte und damit genauere Darstellung zu bekommen, sind Rechenroutinen, die mit BCD-Float-Zahlen umgehen können.
> Die Division hat nichts mit dem Compiler zu tun, sonder mit dem > Präprozessor. Mach es über Variable, dann wird das Ergebnis genauer. Der Präprozessor tastet diesen Code sicher nicht an. Erst der Compiler ist in der Lage, diese Division wegzuoptimieren, d.h. zur Compilezeit auszuführen. Wenn das ungenau wird, liegt es am Compiler. > x = 134.0; > y = 7.0; > i = x / y; Ein guter Compiler wird diese Division ebenfalls zur Compilezeit erkennen und wegoptimieren. Ein volatile könnte helfen.
Hi es nützt doch alles nichts ob das Ding jetzt zur Compilezeit oder zur Laufzeit dividiert wird. Zur Laufzeit wird die Zahl als float dargestellt. Und eine 32 Bit Fließkommazahl hat eben eine Mantisse von 23 Bit und damit eine Genauigkeit von etwa 7 Dezimalstellen. Matthias
Wie oben schon beschrieben wurde, ist die Genauigkeit eine Funktion der Rechenzeit. Die meisten Compiler benutzen die gleiche Genauigeit, wie ANSI C Compiler, z.B. der Keil beim 8051 für 32 Bit Zahlen. Ich selber hab es nicht getestet, aber in mehreren anderen Quellen wurden gesagt, daß der AVR GCC dahingehend Abstriche macht, d.h. zwar relativ schnell ist, aber mit geringerer Genauigkeit rechnet als andere Compiler. Normaler Weise muß die Mantisse 24 Bit, d.h. auf 1,6e7 genau sein. Alle anderen Compiler außer der AVR GCC machen das so. Peter
Hi @peter IEEE 754 Single (und damit der Datentyp float in C) hat 23 Bit Mantisse, 8 Bit Exponent und ein Bit Vorzeichen. Das reicht halt für ~ 7 Dezimalstellen. Warum sollte der GCC da ein eigenes Fließkommaformat einführen? Das passt doch genau zu dem was michi schreibt. 134.0/7.0 stimmt bei ihm bis zur 5. Stelle hinter dem Komma. Das sind 7 Dezimalstellen. Mehr ist mit IEEE 754 Single nicht darstellbar. Matthias
[Matthias:] > beim AVRGCC sind sowohl float als auch double 32 Bit Fließkommazahlen. > Float ist ja schon langsam genug. Double (also 64 Bit) dürfte dann > erst recht übel werden. Na und, das ist doch die Entscheidung des Benutzers! Wenn er es schnell und ungenau will, soll er float nehmen, wenn er es aber genau braucht ohne Rücksicht auf Rechenzeit, dann eben double. Dazu gibt es doch die Unterscheidung. Ein Compiler hat gefälligst nicht den Programmierer zu entmündigen, indem Datentypen sich anders verhalten als vorgesehen. Dann schon lieber eine Fehlermeldung, daß double nicht unterstützt wird, aber einfach float nehmen, ist keine Lösung. (Und wer es wirklich schnell braucht, wird sowieso meist auf Fixkomma ausweichen können).
Es gibt auch C-Compiler für AVR, die 64bit-double bieten und recht flott rechnen. Dafür muß man aber Kiloeuronen freisetzen. Und hin und wieder braucht man tatsächlich dieses Zahlenformat. Wenn man aber z.B. 6-stellige Meßwerte mit 5-stelliger Genauigkeit verrechnen will, kommt man mit 32bit-float völlig aus, wie Du selber oben festgestellt hast.
CodevisionAVR bearbeitet float sogar nur mit 5-stelliger Genauigkeit ab. Aus diesem Grund mußte ich mein Projekt vom mega128 auf einen XC161(16bit) umbauen. Der C166-Compiler von Keil bietet hier float mit 7-Stellen, und float-double-precision mit 14-Stellen Genauigkeit an. Seitdem ist die mathematische Welt wieder in Ordnung. Tipper
Hi @Philipp Sªsse Wenn du das wirklich brauchst dann schnapp dir den Quellcode des AVRGCC und implementier es. @all Warum will man den überhaupt float verwenden? Üblicherweise haben Meßwerte der realen Welt keinen Dynamikumfang der das rechtfertigt. Mit Integer-Arithmetik kann man das meistens genauso gut und schneller erledigen. Matthias
@Matthias Float ist Spitze ! Bevor ich 32bit-Integer verwende, nehme ich lieber float, um Meßwerte zu skalieren. Der Speicherplatz ist der gleiche, float Routinen sind ebenso schnell oder schneller (nur 24bit Mantisse) und es gibt keine Probleme mit trickreichen Skalierungen; das Programm entspricht der Problemstellung und nicht den Tricksereien, die scheinbar besseren Code/Laufzeiten erzeugen. Noch einmal: das float langsam sein muß, ist ein Gerücht !
@Matthias: Isch 'abe aber gar keine AVR-GCC (-: Und double brauche ich schon gar nicht, ich bin immer wunderbar mit Festkomma klargekommen. Aber "ist ja auch zu langsam" als Begründung für eine irreführende Compilerimplementierung, gilt nicht. Da gibt es keine Entschuldigung, da muß "type double not implemented" kommen. @Michael: Kommt auf die Operation an. Für Multiplikationen und Divisionen ist es ziemlich schnurz, okay (wenn durch hohe Dynamik die Mantisse kürzer sein darf als bei Fixkomma, kann es sogar schneller sein!). Aber wenn vor allem addiert und subtrahiert werden soll, ist Fixkomma erheblich schneller. Und für andere Operationen (log, sqrt, ...) exisistieren oftmals sehr schnelle Näherungsverfahren für Fixkomma.
Hi Eine warning wär schon schön, da geb ich dir Recht. Der C-Standard wird an dieser Stelle vom AVRGCC wohl nicht eingehalten. Allerdings scheint mir die Implementierung des AVRGCC für float-Zahlen noch verbesserungswürdig. Der Hardwaremultiplizierer wird nicht verwendet. Wer also wirklich float (schnell) braucht sollte da evtl. mal einen Blick rein werfen. Matthias
@Philipp Ganz klar, wenn int, long oder ein Festkommaformat reichen, soll man das natürlich nehmen. Und auch wenn es atan() mit grober Auflösung sein muß, ist eine Approximation in einer Tabelle deutlich schneller. Mir geht es, wie Du schon sagtest, primär um mul und div und hohe Dynamik. Zum Test habe ich obiges Beispiel (134.0/7.0) auf einem Mega8 mit 16MHz gerechnet: float ca. 38µs und double ca. 110µs. Wählt man andere 'krumme' Zahlen, wackeln die Ausführungszeiten um +/- 10%. Mit double war das Ergebnis mit 15 gültigen Ziffern identisch mit dem von Exel. Bei den recht kurzen Ausführungszeiten ist für mich nicht einzusehen, sich durch skalierte long-Werte Über- oder Unterläufe einzufangen. @Matthias Manchmal juckt es mir auch in den Fingern, die Libs so anzupassen, daß der MUL-Befehl endlich verwendet wird. Aber woher die Zeit nehmen ? Und einem Compiler double als Typ klarzumachen, besteht ja nicht nur darin, die Rechenroutinen selbst zu schreiben; dazu kommen die Routinen, die jedes Format in double und zurück wandeln - mit richtiger Rundung. Der Compiler muß auch die Größe automatisch anpassen und Konstanten als double vorbereitet anlegen. Schließlich will man bei printf() auch noch double finden und nicht auf float reduzierte 6-7 Stellen ausgeben. Und wenn man das dann alles fertig hat und sich zu recht darüber freut, kommt von anderer (ignoranter) Stelle der Kommentar: Sowas braucht man doch garnicht. Das sollte man alles bedenken, bevor man mit der Programmierung anfängt.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.