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. Du hast sogar Glück gehabt, dass das Datenformat zu passen scheint.
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.
Ich glaube verschwenderischer als ASCII-Konvertierung kanns nicht sein. Zahlen von 0-9: 4 bit ASCII Zeichen: 8 bit
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
Also sämtliche Compiler-Dokus behaupten, dass mit long double eine 64bit Gleitkommazahl festgelegt wird.
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.
1 | int FloatBits_longdouble(void) |
2 | {
|
3 | long double x = 1.0, y = 1.0, y_last = 0.0; |
4 | int i = 0; |
5 | while (y != y_last) |
6 | {
|
7 | y_last = y; |
8 | y = 1.0 + x; |
9 | x = x / 2.0; |
10 | i++; |
11 | }
|
12 | return i; |
13 | }
|
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.
:
Bearbeitet durch User
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#Allgemeines Falk 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.
:
Bearbeitet durch User
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.
:
Bearbeitet durch User
nichtgerne schrieb:
...
1 | #include <stdio.h> |
2 | int main() |
3 | { |
4 | double d = 123.1923 ; printf("%.9f\n",d) ; |
5 | float f = 123.1923 ; printf("%.9f\n",f) ; |
6 | return 0 ; |
7 | } |
1 | 123.192300000 |
2 | 123.192298889 |
> 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
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.