Forum: Mikrocontroller und Digitale Elektronik Division zu ungenau


von Michi (Gast)


Lesenswert?

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

von Matthias (Gast)


Lesenswert?

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

von michi (Gast)


Lesenswert?

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

von Matthias (Gast)


Lesenswert?

Hi

exakt. float == double == IEEE 754 Single

Matthias

von Josef (Gast)


Lesenswert?

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

von Leopold Stockinger (Gast)


Lesenswert?

also du meinst
double i, x, y;

main ()
{
  x = 134.0;
  y = 7.0;
  i = x / y;
  ...
  ...
  ...
}

von thkais (Gast)


Lesenswert?

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.

von Chris (Gast)


Lesenswert?

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

von Matthias (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Matthias (Gast)


Lesenswert?

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

von Philipp Sªsse (Gast)


Lesenswert?

[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).

von Michael (Gast)


Lesenswert?

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.

von Tipper (Gast)


Lesenswert?

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

von Matthias (Gast)


Lesenswert?

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

von Michael (Gast)


Lesenswert?

@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 !

von Philipp Sªsse (Gast)


Lesenswert?

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

von Matthias (Gast)


Lesenswert?

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

von Michael (Gast)


Lesenswert?

@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
Noch kein Account? Hier anmelden.