Hallo,
ich rechne gerade mit einem pic32 und möchte die Ergebnisse über USB an
meinem Computer übertragen:
long double ZahlVonInteresse = 123.1923;
uint32 buffer[2];
long double *pointer = (long double*)buffer;
*pointer = ZahlVonInteresse;
....USB_Transmitt(buffer);
Am PC verwandle ich die beiden uint32 werte wieder zurück in ein double
(C#), lasse mir den Wert anzeigen, und erhalte 123.19229888916.
Ich finde das etwas ungenau.
Wo könnte da der Fehler liegen?
Nirgendwo?
Am PC erhalte ich aber folgendes Ergebnis:
double f = 123.1923;
f.ToString() ergibt: 123.1923
Warum kann ich am pc die Zahl exakt darstellen,
im PIC jedoch nicht?
So macht man das nicht. Du weist doch gar nicht ob der PIC und der PC
die gleiche Darstellung von doubles hat. Wenn dann schickt man sowas als
ASCII Klartextstring rueber. So ist es dann egal welches Format der PIC
oder PC hat. Das es bei dir geklappt hat ist wie Hannes schon bemerkte
reines Glueck.
nichtgerne schrieb:> Ah,> ZahlVonInteresse = (long double)123.1923;> dann klappts
klappt doch nicht.
Helmut L. schrieb:> Du weist doch gar nicht ob der PIC und der PC> die gleiche Darstellung von doubles hat.
Ich dachte, die werden immer gleich dargestellt, aber wenn das nicht so
ist,
brauche ich mir also keine weiteren Gedanken machen.
Vielen Dank
nichtgerne schrieb:> Ich dachte, die werden immer gleich dargestellt, aber wenn das nicht so> ist,> brauche ich mir also keine weiteren Gedanken machen.
Meistens ist es das IEEE Format, aber sicher kann man da sich nicht
sein. Deshalb besser in ASCII uebertragen.
Das Binärformat der long doubles ist auf beiden Prozessoren gleich. Da
gibt es kein Problem.
Dein Problem ist, das nicht alle Zahlen exakt als long double
darstellbar sind. Schon bei der Zuweisung:
long double ZahlVonInteresse = 123.1923;
wird die Zahl 123.19229888916 zugewiesen. Dies ist die Zahl, die in long
double darstellbar ist und der 123.1923 am nächsten kommt.
Wenn Du mit dieser Ungenauigkeit nicht leben kannst, dann musst Du dir
ein anderes Zahlenformat suchen. Binary Coded Decimal (BCD) z.B. hat
diese Probleme nicht, ist dafür Verschwenderisch im Umgang mit den Bits
und in der Regel sehr viel langsamer zu berechnen.
Sascha schrieb:> Ich glaube...
Ja. Du glaubst. Allerdings siehst du den Sinn dahinter nicht. Wenn man
schon eine Gleitkommazahl von einem system auf einanderes übertragen
wil, dann bleibt einem garnichts anderes übrig, als sich auf irgend ein
sinnvolles Übertragungsprotokoll zu einigen, was beide Systeme
verstehen. Das ist ASCII.
Bedenke mal, daß man ja auch Zahlen von z.B. 1.23456E34 oder noch größer
übertragen können muß, ebenso eine von System zu System ggf.
unterschiedliche Mantissenlänge. Dazu kommt noch irgend ein
Trennzeichen, damit man weiß, wo eine Zahl endet und die nächste
beginnt.
Abgesehen davon ist es bei Übertragung per USB von der Geschwindigkeit
her ziemlich wurscht, ob man nun "verschwenderisch" oder nicht
überträgt. Ist ohnehin schnell genug und größere Pakete kosten auch
nichr viel mehr an Zeit als kleinere Pakete.
W.S.
Sascha schrieb:> Ich glaube verschwenderischer als ASCII-Konvertierung kanns nicht sein.>> Zahlen von 0-9: 4 bit> ASCII Zeichen: 8 bit
Wie wäre es mit BCD. Damit würdest du pro Ziffer nur knapp 0.68Bit
verschwenden.
Übrigens: Ziffern 0-9 benötigen 3.4 Bit
nichtgerne schrieb:> Am PC verwandle ich die beiden uint32 werte wieder zurück in ein double> (C#), lasse mir den Wert anzeigen, und erhalte 123.19229888916.
Und was kommt auf dem µC raus, wenn du dir dort den Wert der Variablen
ZahlVonInteresse ausgeben läßt.
> Ich finde das etwas ungenau.
Was für ein Format verwendet denn dein Compiler genau für deinen (long
double), i.e. wieviele Bits benutzt der bei dem Datentyp für die
Mantisse? Mit der Angabe kannst du leicht feststellen, wieviel gültige
Stellen dabei rauskommen und welche Stellen aus der Umrechnung in einen
Dezimalbruch resultieren.
W.S. schrieb:> Ja. Du glaubst. Allerdings siehst du den Sinn dahinter nicht. Wenn man> schon eine Gleitkommazahl von einem system auf einanderes übertragen> wil, dann bleibt einem garnichts anderes übrig, als sich auf irgend ein> sinnvolles Übertragungsprotokoll zu einigen, was beide Systeme> verstehen. Das ist ASCII.
Hallo W.S.
Wie kommst Du darauf, das heutzutage irgend jemand etwas anderes als das
IEEE 754 Floating Point Format benutzt? Binäre Übertragung ist völlig in
Ordnung.
Nils schrieb:> Wie kommst Du darauf, das...
Wie kommst du darauf, daß (ja es heißt an dieser Stelle "daß") ein jedes
System "etwas anderes als das IEEE 754 Floating Point Format" benutzt
und obendrein auch die gleiche Endianess besitzt?
Nein, so etwas von vornherein als völlig selbstverständlich anzusehen
ist viel zu kurz gedacht. Deshalb der Umweg über ASCII und
Exponentialdarstellung nebst Trennzeichen, denn das geht IMMER, egal was
für mögliche Unterschiede zwischen den beiden Systemen bestehen mögen.
Wenn du hingegen von deinen Annahmen ausgehst, dan kann das klappen,
muß aber nicht - und du hast beim empfangenden System nicht mal ne
Kontrolle darüber.
W.S.
Nils schrieb:> Dein Problem ist, das nicht alle Zahlen exakt als long double> darstellbar sind. Schon bei der Zuweisung:>> long double ZahlVonInteresse = 123.1923;>> wird die Zahl 123.19229888916 zugewiesen. Dies ist die Zahl, die in long> double darstellbar ist und der 123.1923 am nächsten kommt.
Das die Zahl eventuell nicht exakt als long-double darstellbar ist,
hätte ich mir auch vorstellen können. Dass aber bereits die dritte
Nachkommastelle unterschiedlich ist, finde ich halt ziemlich "ungenau".
Darüberhinaus versteh ich dann nicht, wieso der PC die Zahl 123.1923
exakt festhalten kann, wenn in beiden Fällen mit 64bit Gleitkommazahlen
gerechnet wird (long double beim PIC, XC32 Comiler; double im PC, C#).
Mir gehts eigentlich gar nicht darum, irgendwelche Zahlen zum PC zu
übertragen (zumindest nicht in übertriebener Genauigkeit). Ich will nur
wissen, warum es nicht klappt.
nichtgerne schrieb:> Dass aber bereits die dritte> Nachkommastelle unterschiedlich ist, finde ich halt ziemlich "ungenau".
Dann runde mal die 123.19229888916 auf drei Nachkommastellen. Für mich
sieht das sehr nach 123.192 aus.
Bist du sicher, dass dein Compiler auf dem Pic dein "long double" nicht
geflissentlich ignoriert und statt dessen mit single rechnet oder
zumindest deine Konstante als single rein holt.
Wolfgang schrieb:> Bist du sicher, dass dein Compiler auf dem Pic dein "long double" nicht> geflissentlich ignoriert und statt dessen mit single rechnet oder> zumindest deine Konstante als single rein holt.
Ich denke, deutlicher als durch "long double" kann ich dem Compiler
nicht sagen, dass ich 64bit will.
Und mit ZahlVonInteresse = (long double)123.1923;
wollte ich dem Compiler mitteilen, dass er die Zahl nicht als single
reinholt.
Bie Mikroontrollern herrscht ein ziemlicher Wildwuchs bezüglich
Fließkommaformaten. Bei 8Bit Rechnern ist meistens double das Gleiche
wie float(32bit). Beim PIC32 ist wohl long double das gleich wie double
(64bit). Von wegen ist doch alles genormt wie einer hier schrieb.
Und dann kommt noch die Ausgaberoutine. Wer weiß was die aus double
macht?
nichtgerne schrieb:> Ich denke, deutlicher als durch "long double" kann ich dem Compiler> nicht sagen, dass ich 64bit will.
Die Frage war nicht, was du ihm sagst, sondern was er draus macht.
Die wahre Auflösung deiner "long double" Variablen kannst du aber mit
einem sprichwörtlichen Dreizeiler herausbekommen, indem du in einer
Schleife die Folge y=(1+x) mit immer halbierendem x berechnest, und
guckst, ab welchem x sich y nicht mehr ändert.
Guck doch mal in den objektdump deines MIPS compilers ob er 4Byte oder 8
Byte für die Variable und Konstante im Speicher vorsieht.
Zumindest der GCC macht bei double aufm MIPS auch wirklich double.
long double ist beim MIPS GCC jedenfalls nicht 80bit sondern double
(64bit).
Sonst würde dein Code da oben auch nicht funktionieren, da er nur 64bit
überträgt.
Ansonsten ist es schon komisch, dass nach der dritten Kommastelle der
Wert falsch is (wenns denn witklich double ist).
Das hier ist nen MIPS und die Taschenrechneranwendung weicht auch bei
der siebten Nachkommastelle nicht ab:
http://www.fritzler-avr.de/spaceage2/index.htm
nichtgerne schrieb:> Also sämtliche Compiler-Dokus behaupten, dass mit> long double eine 64bit Gleitkommazahl festgelegt wird.
Dann probiers doch einfach aus, statt dich durch Stapel von Papier zu
wühlen.
nichtgerne schrieb:> Also sämtliche Compiler-Dokus behaupten, dass mit> long double eine 64bit Gleitkommazahl festgelegt wird.
Mit anderen Worten: Der Compiler ignoriert das "long" und verwendet
einfache den Typ double.
Sascha schrieb:> Ich glaube verschwenderischer als ASCII-Konvertierung kanns nicht sein.
Naja, geht so.
sizeof(double) = 8 Bytes
"123.1923" sind 8 Zeichen. Mit einer Ende-Markierung dann 9.
W.S. schrieb:
> Wenn man schon eine Gleitkommazahl von einem system auf einanderes> übertragen wil, dann bleibt einem garnichts anderes übrig, als sich auf> irgend ein sinnvolles Übertragungsprotokoll zu einigen, was beide Systeme> verstehen. Das ist ASCII.
ASCII ist nicht das einzige Textformat. Gut, es ist unwahrscheinlich,
dass das Zielsystem das nicht benutzt (bzw. versteht). Das gilt aber für
die IEEE singe/double precision heutzutage auch.
W.S. schrieb:> Wie kommst du darauf, daß (ja es heißt an dieser Stelle "daß")
Streng genommen heißt es "dass".
> ein jedes System "etwas anderes als das IEEE 754 Floating Point Format"> benutzt und obendrein auch die gleiche Endianess besitzt?
Es muss nicht jedes System können, sondern nur die an der Kommunikation
beteiligten. Das ist hier offenbar der Fall. Wenn erst die siebte
signifikante Stelle nicht den erwarteten Wert hat, ist es doch völliger
Unsinn zu behaupten, das läge an unterschiedlichen
Floating-Point-Formaten oder die Endianness könnte falsch sein.
> Nein, so etwas von vornherein als völlig selbstverständlich anzusehen> ist viel zu kurz gedacht.
Das ist allerdings richtig.
nichtgerne schrieb:> Das die Zahl eventuell nicht exakt als long-double darstellbar ist,> hätte ich mir auch vorstellen können. Dass aber bereits die dritte> Nachkommastelle unterschiedlich ist,
Wieviele Stellen nach dem Komma stehen, ist bei einem Gleitkomma-Typ
völlig irelevant, davon abgesehen, dass es nicht die dritte, sondern die
vierte ist. Wichtig ist, die wievielte Stelle es insgesamt ist, und da
zähle ich den Fehler erst in der siebten Stelle.
> Darüberhinaus versteh ich dann nicht, wieso der PC die Zahl 123.1923> exakt festhalten kann,
Er kann sie nicht exakt festhalten. Er rundet nur die Ausgabe, so dass
du den Fehler nicht siehst.
> Mir gehts eigentlich gar nicht darum, irgendwelche Zahlen zum PC zu> übertragen (zumindest nicht in übertriebener Genauigkeit). Ich will nur> wissen, warum es nicht klappt.
Werden denn alle Bytes korrekt übertragen? Wenn da eins fehlt, können
solche Fehler auch auftreten.
nichtgerne schrieb:> Ich denke, deutlicher als durch "long double" kann ich dem Compiler> nicht sagen, dass ich 64bit will.
Was du willst, ist dem Compiler allerdings herzlich egal. Was er laut
Spezifikation umsetzt, ist entscheidend.
> Und mit ZahlVonInteresse = (long double)123.1923;> wollte ich dem Compiler mitteilen, dass er die Zahl nicht als single> reinholt.
123.1923 ist vom Typ double. Das konvertierst du dann nachträglich in
einen long double. Das gleiche ist aber ohne den Cast auch schon ganz
automatisch passiert beim Schreiben in die Variable. Wenn die Konstante
selbst vom Typ long double sein soll, musst du 123.1923L schreiben.
Vorausgesetzt, dein Compiler setzt das alles entsprechend ISO-Norm um.
Wolfgang schrieb:> nichtgerne schrieb:>> Also sämtliche Compiler-Dokus behaupten, dass mit>> long double eine 64bit Gleitkommazahl festgelegt wird.>> Mit anderen Worten: Der Compiler ignoriert das "long" und verwendet> einfache den Typ double.
Naja, was heißt "ignoriert"? Er hat nicht die Verpflichtung, long double
größer zu machen als double.
Rolf M. schrieb:> Wieviele Stellen nach dem Komma stehen, ist bei einem Gleitkomma-Typ> völlig irelevant, davon abgesehen, dass es nicht die dritte, sondern die> vierte ist. Wichtig ist, die wievielte Stelle es insgesamt ist, und da> zähle ich den Fehler erst in der siebten Stelle.
Ja, beim Zählen mache ich oft Fehler.
Rolf M. schrieb:>> Darüberhinaus versteh ich dann nicht, wieso der PC die Zahl 123.1923>> exakt festhalten kann,>> Er kann sie nicht exakt festhalten. Er rundet nur die Ausgabe, so dass> du den Fehler nicht siehst.
Woher sollte den der Computer wissen, dass er auf 4 Nachkommastellen
runden soll?
Ihr wundert euch über die Ungenauigkeit schon bei der dritten Stelle?
Das Problem ist, das halt nur wenige Dezimalzahlen genau auf das binäre
Format von Floating Point mappen:
void test (void)
{
volatile float x = 1.1;
printf ("%2.12f\n", x);
}
Gibt 1.100000023842 aus.
Nils schrieb:> Ihr wundert euch über die Ungenauigkeit schon bei der dritten Stelle?
Bei Float ist es für die Genauigkeit völlig egal, wo das Komma sitzt. Es
kommt auf die Gesamtzahl der gültigen Stellen an.
Eine Double-Zahl nach IEEE 754 enthält 52 (+implizit 1) Bit für die
Mantisse. In Dezimaldarstellung kommt man damit auf 15..16 signifikante
Stellen, kein Grund also, bereits in der 7ten Stelle abzuweichen.
Die Abweichung spricht dafür, dass da irgendwo eine Operation mit
Single-Typ (7..8 gültige Stellen) statt findet, die die Genauigkeit
beschneidet.
Das Abbilden von Dezimalzahlen auf 64-Bit Float beschränkt niemals die
Genauigkeit auf 7 Stellen.
Wolfgang schrieb:> Bei Float ist es für die Genauigkeit völlig egal, wo das Komma sitzt. Es> kommt auf die Gesamtzahl der gültigen Stellen an.
Hallo Wolfgang,
warum kann ich dann 1.1 nicht exakt darstellen, 11 aber sehr wohl? Das
Komma ist doch nur um eine Stelle verschoben?
@Nils (Gast)
>warum kann ich dann 1.1 nicht exakt darstellen, 11 aber sehr wohl?
Weil die Fließkommazahlen als Dezimalbruch mit der Basis 2 gespeichert
werden, nicht mit der Basis 10. Da gibt es immer wieder ein paar Lücken,
auch wenn die eher klein sind.
Nils schrieb:> warum kann ich dann 1.1 nicht exakt darstellen, 11 aber sehr wohl?
Gegenfrage: Warum ist in float 16777217 nicht exakt darstellbar, 1,5
aber sehr wohl, genauso wie 1,0625?
Falk B. schrieb:> @Nils (Gast)>>>warum kann ich dann 1.1 nicht exakt darstellen, 11 aber sehr wohl?>> Weil die Fließkommazahlen als Dezimalbruch mit der Basis 2 gespeichert> werden,
Das können sie zwar auch, werden sie aber in der Regel nicht.
https://de.wikipedia.org/wiki/IEEE_754#AllgemeinesFalk B. schrieb:> Da gibt es immer wieder ein paar Lücken, auch wenn die eher klein sind.
Da gibt es jede Menge Lücken, die quasi unendlich groß sind, in dem
Sinne, dass jede dieser Lücken unendlich viele Werte umfaßt.
Nils schrieb:> warum kann ich dann 1.1 nicht exakt darstellen
Weil 0,1 der Wert des Bruchs 1/10 ist, und der ist im Binärsystem nicht
darstellbar. Genauso wie der Wert des Bruchs 1/3 im Dezimalsystem nicht
darstellbar ist, der schulmässige Ausweg mit unendlich vielen 3en nach
dem Komma passt in keinen realen Computer und auch auf kein Papier.
In der Praxis trifft einen dann beides: Werte wie 1/3 kann man nicht
exakt eingeben, und Werte wie 1/10 kann der Computer nicht exakt
speichern. Und ein anderes Zahlensystem hilft auch nicht weiter, da sind
es nur andere Brüche die nicht darstellbar sind. Das liegt wohl an der
Unvollkommenheit der Welt an sich.
Georg
Rolf M. schrieb:> Falk B. schrieb:> ...>> Weil die Fließkommazahlen als Dezimalbruch mit der Basis 2 gespeichert>> werden,>> Das können sie zwar auch, werden sie aber in der Regel nicht.> https://de.wikipedia.org/wiki/IEEE_754#Allgemeines
Was meinst du denn, was dort heißt:
1
"Basis b (bei normalisierten Gleitkommazahlen nach IEEE 754 ist b=2)"
Wolfgang schrieb:> Rolf M. schrieb:>> Falk B. schrieb:>> ...>>> Weil die Fließkommazahlen als Dezimalbruch mit der Basis 2 gespeichert>>> werden,>>>> Das können sie zwar auch, werden sie aber in der Regel nicht.>> https://de.wikipedia.org/wiki/IEEE_754#Allgemeines>> Was meinst du denn, was dort heißt:"Basis b (bei normalisierten> Gleitkommazahlen nach IEEE 754 ist b=2)"
Es ging mir selbstverständlich nicht um die Basis 2, sondern darum, dass
da nirgends ein Dezimalbruch gespeichert wird. Gerade das ist ja auch
der Grund, warum 0,1 eben nicht exakt darstellbar ist. Bei einem
Dezimalbruch wäre es das nämlich.
> Wo könnte da der Fehler liegen?
Möglicherweise dort:
> Am PC verwandle ich die beiden uint32 werte wieder zurück in ein double> (C#), lasse mir den Wert anzeigen, und erhalte 123.19229888916.
Wie erfolgt die Konvertierung? Wie erfolgt die Ausgabe? Binärformate
verglichen?
> Also sämtliche Compiler-Dokus behaupten, dass mit> long double eine 64bit Gleitkommazahl festgelegt wird.https://en.wikipedia.org/wiki/Long_double