Forum: Mikrocontroller und Digitale Elektronik Verständnisfrage IEEE754 float in dezimal umrechnen


von Ralf (Gast)


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

von Frank (Gast)


Lesenswert?

Vielleicht verstehe ich dein Problem grad nicht, aber wenn du nur von 
float nach dezimal haben willst, dann weise doch dem int einfach den 
float zu.

von Falk B. (falk)


Lesenswert?

@ Ralf (Gast)
>ies theoretisch zu tun ist. Ich möchte die in "C" tun.

Was willst du da rechnen? Das macht der Compiler für dich.
1
float af;
2
int ai;
3
4
af = ai;
5
ai = af;
6
7
print_f ("Integer %i, Float, %f \r\n", ai, af);

von Yalu X. (yalu) (Moderator)


Lesenswert?

Oder meinst du mit "dezimal" die BCD-Darstellung, wo jede Dezimalziffer 
der Zahl als Gruppe von 4 Bits dargestellt wird (wie bei den meisten 
Taschenrechnern üblich)?

von K2R (Gast)


Lesenswert?

Leute, es manchmal auch jemanden, der's einfach nur kapieren will....

Ralf, schau mal hier:

Das ist der Standard
http://754r.ucbtest.org/standards/754.pdf

und hier ist's imho schön erklärt (step by step)
http://de.wikipedia.org/wiki/IEEE_754

von floatexperte (Gast)


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

von Ralf (Gast)


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

von Falk B. (falk)


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.

von Ralf (Gast)


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

von holger (Gast)


Lesenswert?

>fTmpZahl = uiTmpZahl;

Das geht so nicht;) Mit ner Union könnte man das lösen.

von Peter II (Gast)


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;

von Ralf (Gast)


Lesenswert?

holger schrieb:
>>fTmpZahl = uiTmpZahl;
>
> Das geht so nicht;) Mit ner Union könnte man das lösen.

Hallo Holger,

Kannst Du mal ein Beispiel angeben? Ich hab unions noch nie 
gebraucht/benutzt :-)

Gruß
Ralf

von Falk B. (falk)


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/

von Helmut S. (helmuts)


Lesenswert?

Hallo Ralf,
zeig doch mal die 4 Bytes und sag dazu welche Zahl du erwartest.

von Ralf (Gast)


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

von Peter II (Gast)


Lesenswert?

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.

von Ralf (Gast)


Lesenswert?

Helmut S. schrieb:
> Hallo Ralf,
> zeig doch mal die 4 Bytes und sag dazu welche Zahl du erwartest.

Hallo Helmut,

die übertragene Zahl wird leider nicht angezeigt. Sie müsste aber keiner 
0 sein.

Ralf

von Falk B. (falk)


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.

von Ralf (Gast)


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

von Helmut S. (helmuts)


Lesenswert?

Ralf,
sag doch endlich mal die 4 Bytes oder sind die geheim?

von Ralf (Gast)


Lesenswert?

Falk Brunner schrieb:

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

Jepp :-) Deshalb poste ich hier ja auch.

Auf jeden Fall habe ich jetzt ne Menge Anregungen und wer es morgen 
ausprobieren.

Danke soweit an Euch alle.

Ralf

von Peter II (Gast)


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.

von Ralf (Gast)


Lesenswert?

Helmut S. schrieb:
> Ralf,
> sag doch endlich mal die 4 Bytes oder sind die geheim?

Nein sind sie nicht, Sie ändern sich nur standig mit jeder Übertragung 
und ich hab die Apperatur jetzt auch nicht griffbereit :-)

Ralf

von Ralf (Gast)


Lesenswert?

Peter II schrieb:

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

Den cast habe ich unterschlagen. Vieleicht gehts dann ja schon.

Ralf

von Peter II (Gast)


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.

von Ralf (Gast)


Lesenswert?

Peter II schrieb:

> nimm memcpy dann ist es offensichtlich was gemacht wird.

o.k. Überredet:-)

von floatexperte (Gast)


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.

von Falk B. (falk)


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.

von Ralf (Gast)


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

von Falk B. (falk)


Lesenswert?

@ Ralf (Gast)

>Ich habe es jetzt mit memcpy gemacht und es funst auf anhieb: :-)

OK, probier aber auch mal den cast.

fTmpZahl = *((float*)RxBuffer);

von Karl H. (kbuchegg)


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?

: Bearbeitet durch User
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.