long double übertragen

Gast #4457130
Lesenswert?

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

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

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

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

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

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

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

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

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

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

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.
(Firma: fritzler-avr.de) #4457454
Lesenswert?

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

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
}
#4457613
Lesenswert?

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

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

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

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

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

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

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

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

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