Verständnisfrage IEEE754 float in dezimal umrechnen

Gast #3512015
Lesenswert?

Hallo,

ich möchte eine 32 Bit Zahl, die nach IEEE754 codiert ist, nach dezimal 
umrechnen. Ich habe dazu auch schon etliche Anleitungen gefunden wie 
dies theoretisch zu tun ist. Ich möchte die in "C" tun.

Klar ist mit der Aufbau:

Sign    Exponent    Mantisse
0       00000000    00000000000000000000000

Das Vorzeichen bestimmt ob die Zahl Pos. oder Neg. ist. Also nach der 
Konvertierung mit 1 oder -1 multiplizieren. - verstanden!

Der Exponent wird mit "0x7F800000" maskiert und dann mit ( >> 23 ) nach 
"unten" geshiftet. Zuletzt werden noch 127 abgezogen. - auch soweit 
klar!

Die Mantisse wird mit "0x007FFFFF" maskiert. Sie soll den Teil nach dem 
"," darstellen. -auch noch klar

Es soll jetzt die weggelassene "1" vor dem Komma wieder hinzugefügt 
werden. Also z.B. "1,010010110101". Hier habe ich mein größtes Problem: 
Wie soll ich das im C Code abbilden? Ich kann doch keine binäre 
Kommazahl erstellen????

Dann soll meine Zahl = Vorzeichen*Mantisse*2^Exponent sein. - auch 
logisch

Also würde ich dann 2*2*2*2*2 ... Exponenten mal schreiben. - auch klar

Dann muss die Zahl, die immer noch binär ist dezimal gewandelt werden. 
Was sich mir auch nicht wirklich erschließt. Scheint aber nach dem 
Muster "8,4,2,1, 0.??" zu gehen?

Ich habe schon mehr als 5 Anleitugen im Netz durch, habe aber den 
Vorgang immer noch nicht verstanden. Vor allem die Umsetzung in "C".

Kann mir ein geduldiger Mensch diese Umwandlung näherbringen oder anhand 
von einem Codebeispiel erklären? Würde mich sehr freuenc :-)

Danke und Gruß
Ralf
Gast #3512176
Lesenswert?

Ralf schrieb:
> Scheint aber nach dem Muster "8,4,2,1, 0.??" zu gehen?

Die Wertigkeiten sind so:

... 2^2 2^1 2^0 , 2^-1 2^-2 ...

Der ausgerechnete Exponent gibt ja im Grunde nur die Verschiebung des 
Kommas in der binär dargestellten Zahl an.

Dein Beispiel
1,010010110101
Nehmen wir an der exponent ist 128. Also 128 - 127 = 1. Daraus folgt als 
Endergebnis:

10,10010110101

Also Dezimal:
2,5883789063
Gast #3512292
Lesenswert?

Hallo,

danke für Eure Antworten.

@Frank und Falk
Ja, eigentlich würde das mein Compiler machen, aber:
Ich empfange über die serielle Schnittstelle eine nach IEEE754 codierte 
4 Bit float zahl. Mein Compiler setzt die floats aber anders zusammen:

"The CrossWorks C library uses IEEE floating point format as specified 
by the ISO 60559 standard with restrictions"

Wenn ich den empfangenen Wert einfach einem float zuweise kommt nur Müll 
raus. Habe auch wegen MSB first und LSB first die Bytes testweise 
getauscht.

@Yalu
Nein meine ich nicht :-) Vieleicht habe ich mich auch nicht klar 
ausgedrückt.

@K2R
Danke für die Links. Bei Wiki ist es wirklich gut erklärt. Die Frage ist 
nur nach der schlanken Umsetzung in C.

@floatexperte
Kann der Exponent eigentlich auch negativ werden? Also <= 127 ?

Hat jemand mal so eine Umrechnung gemacht und hat in Beispiel? Ich 
befürchte die Umrechnung wird echt aufwändig.

Oder hat jemand eine andere tolle Idee??? :-)

Gruß
Ralf
#3512356
Lesenswert?

@ Ralf (Gast)

>Ich empfange über die serielle Schnittstelle eine nach IEEE754 codierte
>4 Bit float zahl. Mein Compiler setzt die floats aber anders zusammen:

Möglich, aber unwahrscheinlich.

>"The CrossWorks C library uses IEEE floating point format as specified
>by the ISO 60559 standard with restrictions"

Kenn den Standrad nicht, ich tippa ber auch das gleiche Format wie der 
Rest der C Compiler dieser Welt.

>Wenn ich den empfangenen Wert einfach einem float zuweise kommt nur Müll
>raus. Habe auch wegen MSB first und LSB first die Bytes testweise
>getauscht.

Zu 99% liegt der Fehler bei dir. Poste Code.
Gast #3512392
Lesenswert?

Hallo Falk,

der fragliche Codeausschnitt sieht so aus:
Im RxBuffer[] stehen der Reihe nach die Empfangenen Bytes. In [0] das 
Erste und in [3] das Letzte.
1
float     fTmpZahl = 0;
2
uint32_t  uiTmpZahl = 0;
3

4
uiTmpZahl = (uint32_t)RxBuffer[0] + ((uint32_t)RxBuffer[1] << 8) + ((uint32_t)RxBuffer[2] << 16) + ((uint32_t)RxBuffer[3] << 24);
5

6
fTmpZahl = uiTmpZahl;

In "uiTmpZahl" werden die Bytes richtig eingetragen. Ich habe auch schon 
die Bytereihenfolge getauscht.

Gruß
Ralf
Gast #3512409
Lesenswert?

Ralf schrieb:
> float     fTmpZahl = 0;
> uint32_t  uiTmpZahl = 0;
>
> uiTmpZahl = (uint32_t)RxBuffer[0] + ((uint32_t)RxBuffer[1] << 8) +
> ((uint32_t)RxBuffer[2] << 16) + ((uint32_t)RxBuffer[3] << 24);
>
> fTmpZahl = uiTmpZahl;

so macht man das auch nicht. Wenn in RXbuffer ein Float steht muss man 
es auch als float behandeln.


float     fTmpZahl = 0;
memcpy( &fTmpZahl, RxBuffer, 4 );

uint32_t  uiTmpZahl = fTmpZahl;
#3512416
Lesenswert?

@ Ralf (Gast)

>Im RxBuffer[] stehen der Reihe nach die Empfangenen Bytes. In [0] das
>Erste und in [3] das Letzte.

;-)
wusste ich es doch. Soooo geht es natürlich NICHT!
Eher so
1
float     fTmpZahl = 0;
2
float     *ptmp;
3

4
  ptmp =(float*)RxBuffer;
5
  fTmpZahl = *ptmp;
6

7
   // das könnte auch klappen
8
  fTmpZahl = *((float*)RxBuffer);

GGf. stimmt die Reihenfolge der Bytes nicht, dann musst du die vorher 
tauschen.

Prüfen kannst du deine Rechung, indem du die vier Bytes einzeln ausgeben 
lässt und durch einen Onlinerechner schickst

http://gregstoll.dyndns.org/~gregstoll/floattohex/
Gast #3512420
Lesenswert?

Peter II schrieb:

> so macht man das auch nicht. Wenn in RXbuffer ein Float steht muss man
> es auch als float behandeln.
>
> float     fTmpZahl = 0;
> memcpy( &fTmpZahl, RxBuffer, 4 );
>
> uint32_t  uiTmpZahl = fTmpZahl;

Hallo Peter II,

was macht den memcpy in dem Fall anders als das shiften? Ich gebe zu, 
das das memcpy eleganter ist :-) Ich shifte aber nun mal so gerne :-)

Ralf
#3512438
Lesenswert?

@ Ralf (Gast)

>was macht den memcpy in dem Fall anders als das shiften?

Was der Name sagt. Kopieren.

> Ich gebe zu,
>das das memcpy eleganter ist :-) Ich shifte aber nun mal so gerne :-)

Du hast das Zahlenformat float noch nicht verstanden und auch nicht 
seine Repräsentation im Speicher.

Mach mal deine Rechnung in Einzelschritten im Simulator bzw. auf Papier. 
Dann siehst du, was für ein Unsinn du macht. Die Zahl kannst du ja als 
Float vorgeben.
Gast #3512439
Lesenswert?

Peter II schrieb:
> Ralf schrieb:
>> was macht den memcpy in dem Fall anders als das shiften? Ich gebe zu,
>> das das memcpy eleganter ist :-) Ich shifte aber nun mal so gerne :-)
>
> es kopiert speicher. floats kann man nicht shiften.

Ich shifte doch gar kein float. Der Buffer besteht aus 4 Bytes die 
hintereinander eintrudeln. Mit dem shiften schiebe ich die Bytes einfach 
an die Positionen in der uint32 Variablen.
Ich werd es auf jeden Fall testen. Der Unterschied im Speicher ist mir 
aber nicht klar. Ich kann es mir morgen mal im Debugger ansehen.

Ralf
Gast #3512448
Lesenswert?

Ralf schrieb:
> Mit dem shiften schiebe ich die Bytes einfach
> an die Positionen in der uint32 Variablen.

richtig, damit hast du ein float in einen int. Im schlimmsten fall in 
der falschen Reihenfolge weil sich big und Little endian unterscheiden.

Wenn die Reihenfolge richtig ist, müsste man nur jetzt casten.
1
float     fTmpZahl = 0;
2
uint32_t  uiTmpZahl = 0;
3

4
uiTmpZahl = (uint32_t)RxBuffer[0] + ((uint32_t)RxBuffer[1] << 8) + ((uint32_t)RxBuffer[2] << 16) + ((uint32_t)RxBuffer[3] << 24);
5

6
fTmpZahl = *((float*)&uiTmpZahl);

aber das umständlicher, an fehleranfälliger.
Gast #3512461
Lesenswert?

Ralf schrieb:
> Den cast habe ich unterschlagen. Vieleicht gehts dann ja schon.

es wird nicht immer gehen wegen der Byte Reihenfolge. Dein shift liefert 
auf ARM was anderes als auf x86. Dann klappt aber auch der cast nicht.

nimm memcpy dann ist es offensichtlich was gemacht wird.
Gast #3512482
Lesenswert?

Ralf schrieb:
> Kann der Exponent eigentlich auch negativ werden? Also <= 127 ?

Ja natürlich.

> Hat jemand mal so eine Umrechnung gemacht und hat in Beispiel? Ich
> befürchte die Umrechnung wird echt aufwändig.

Warum? Die Rechnung geht ganz exakt genau so wie oben beschrieben. Es 
wird halt nur das Komma (in der Binärdarstellung) in die andere Richtung 
(nach links) verschoben.
#3512687
Lesenswert?

@ Ralf (Gast)

>hintereinander eintrudeln. Mit dem shiften schiebe ich die Bytes einfach
>an die Positionen in der uint32 Variablen.

Ja. Aber dann kann man KEINE direkte Zuweisung an eine Float-Variable 
machen, denn dann INTERPRETIERT der Compiler deine uint32 Zahl als eben 
uint32. In diesem Fall liegt der Trick eben darin, den Compiler zu 
überlisten und NUR Daten zu kopieren OHNE sie zu INTERPRETIEREN. Erst 
wenn das vorbei ist, darf er wieder interpretieren.

Klingt verwirrend, geb ich zu ;-)

>Ich werd es auf jeden Fall testen. Der Unterschied im Speicher ist mir
>aber nicht klar.

Dein rxbuffer hat im Speicher die gleiche Reihenfolge wie dein uint32 
nach dem Zusammensetzen über die Shiftoperation. Das Problem ist 
hierbei, dass bei einer Zuweisung variable float = variable int oder 
anders herum der Compiler immer INTERPRETIERT und eine passende 
Umwandlungsfunktion aufruft. Es werden NICHT einfach nur 4 Bytes 
kopiert! Aber genau DAS ist nötig, wenn man einen Float aus vier 
einzelnen Bytes zusammensetzen will!
Kopieren OHNE umzuwandeln. Denn es IST ja schon ein Float. Man muss die 
4 Bytes nur am Compiler vorbei in die Variable "mogeln".

Ich hoffe das ist ein wenig verständlich.
Gast #3512723
Lesenswert?

Hallo Falk,

danke für Deine Erklärung.

Ich habe es jetzt mit memcpy gemacht und es funst auf anhieb: :-)
1
memcpy(&f32Zahl,&RxBuffer[0],4);

Die Bytes im Buffer sind: 
RxBuffer[0]=0B,RxBuffer[1]=13,RxBuffer[2]=28,RxBuffer[3]=40.

In der 32Bit float Zahl:
0x4028130B

Die getauschte Reihenfolge hatte ich ja bereits getestet. Es liegt 
wirklich am "zu schlauen" compiler :-)

Danke noch mal an alle bei der Erklärung und Lösungsfindung. Wieder was 
gelernt :-)

Gruß
Ralf
#3512820
Lesenswert?

Ralf schrieb:

> Danke noch mal an alle bei der Erklärung und Lösungsfindung. Wieder was
> gelernt :-)

Und?
Hast du das Gefühl, das da irgendetwas seltsames passiert oder ist dir 
100% klar was in deiner Version passiert ist, was in der jetzigen 
Version passiert und vor allen Dingen warum das jetzt das korrekte 
Ergebnis liefert?

Denn im Kern geht es um die Fragestellung
1
  int i = 5;
2
  double d;
3

4
  d = i;
was passiert eigentlich bei der Zuweisung. Wie geht der Compiler damit 
um, dass links vom = ein double steht, während rechts vom = ein int 
steht? Was muss da passieren?
Und warum ist das, was normalerweise absolut erwünscht ist, in deinem 
Fall völlig kontraproduktiv?

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