Forum: Mikrocontroller und Digitale Elektronik long double übertragen


von nichtgerne (Gast)


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?

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Nirgendwo. Du hast sogar Glück gehabt, dass das Datenformat zu passen 
scheint.

von nichtgerne (Gast)


Lesenswert?

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?

von nichtgerne (Gast)


Lesenswert?

Ah,

ZahlVonInteresse = (long double)123.1923;

dann klappts

von Helmut L. (helmi1)


Lesenswert?

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.

von nichtgerne (Gast)


Lesenswert?

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

von Helmut L. (helmi1)


Lesenswert?

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.

von Nils (Gast)


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.

von Sascha (Gast)


Lesenswert?

Ich glaube verschwenderischer als ASCII-Konvertierung kanns nicht sein.

Zahlen von 0-9: 4 bit
ASCII Zeichen: 8 bit

von W.S. (Gast)


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.

von Wolfgang (Gast)


Lesenswert?

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

von Wolfgang (Gast)


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.

von Nils (Gast)


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.

von W.S. (Gast)


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.

von W.S. (Gast)


Lesenswert?

Nachtrag: nie etwas anderes..

von nichtgerne (Gast)


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.

von Wolfgang (Gast)


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.

von nichtgerne (Gast)


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.

von Helmut S. (helmuts)


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?

von Wolfgang (Gast)


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.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


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

von nichtgerne (Gast)


Lesenswert?

Also sämtliche Compiler-Dokus behaupten, dass mit
long double eine 64bit Gleitkommazahl festgelegt wird.

von Wolfgang (Gast)


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
}

von Wolfgang (Gast)


Lesenswert?

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.

von Rolf M. (rmagnus)


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.

: Bearbeitet durch User
von nichtgerne (Gast)


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?

von Nils (Gast)


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.

von Wolfgang (Gast)


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.

von Nils (Gast)


Lesenswert?

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?

von Falk B. (falk)


Lesenswert?

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

von Rolf M. (rmagnus)


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.

: Bearbeitet durch User
von Georg (Gast)


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

von Wolfgang (Gast)


Lesenswert?

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)"

von Rolf M. (rmagnus)


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.

: Bearbeitet durch User
von Mikro 7. (mikro77)


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

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.