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
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)?
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
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
@ 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.
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.
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;
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
@ 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
floatfTmpZahl=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/
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
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.
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
@ 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.
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
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
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.
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
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.
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.
@ 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.
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
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
inti=5;
2
doubled;
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?