Codierung des int-Werts als IEEE-754-Float (binary32)
Lesezeit: 7 Minuten
Anonym
Wie kann angesichts der 32 Bits, die eine IEEE 754-Gleitkommazahl darstellen, die Zahl in eine Ganzzahl konvertiert werden, indem Ganzzahl- oder Bitoperationen für die Darstellung verwendet werden (anstatt einen Maschinenbefehl oder eine Compileroperation zum Konvertieren zu verwenden)?
Ich habe die folgende Funktion, aber sie schlägt in einigen Fällen fehl:
Eingabe: int x (enthält eine 32-Bit-Zahl mit einfacher Genauigkeit im IEEE 754-Format)
if(x == 0) return x;
unsigned int signBit = 0;
unsigned int absX = (unsigned int)x;
if (x < 0)
{
signBit = 0x80000000u;
absX = (unsigned int)-x;
}
unsigned int exponent = 158;
while ((absX & 0x80000000) == 0)
{
exponent--;
absX <<= 1;
}
unsigned int mantissa = absX >> 8;
unsigned int result = signBit | (exponent << 23) | (mantissa & 0x7fffff);
printf("\nfor x: %x, result: %x",x,result);
return result;
Dies wirft keinen Float in ein int. Es kopiert nur bitweise ihre Maschinendarstellung, ohne zB zu konvertieren 2.03e1 zu 20 [by rounding] als die (int)2.03e1 gegossen wird.
– Basile Starynkevitch
9. September 2012 um 20:59 Uhr
Du will tun es bitweise? Nun, so machen Sie es bitweise – es interpretiert nur die Bytes neu. Keine Schritte, wirklich.
– Ry- ♦
9. September 2012 um 21:00 Uhr
Aber 0x7eff8965 = 1325268755 (nach dem Casting). Wenn Sie das HEX in IEEE 754 Calc verwenden, erhalten Sie 1.6983327e+38 und HEX zu Dezimal ergibt: 2130676069 – keines von ihnen gibt das korrekte Ergebnis von 1325268755.
– Anonym
9. September 2012 um 21:04 Uhr
Dieser Code hat ein undefiniertes Verhalten in C. Siehe Abschnitt 6.5 im Standard.
– Paul Hankin
9. September 2012 um 21:30 Uhr
Übrigens konvertiert der von Ihnen gepostete Code ein 32-Bit-Signed-Int in sein 32-Bit-IEEE 754-Single-Precision mit Rundung in Richtung Null. Ich weiß es, weil ich es gestern geschrieben habe.
– Gabe
10. September 2012 um 4:45 Uhr
C hat die “Vereinigung”, um diese Art von Datenansicht zu handhaben:
typedef union {
int i;
float f;
} u;
u u1;
u1.f = 45.6789;
/* now u1.i refers to the int version of the float */
printf("%d",u1.i);
Dies ist ein undefiniertes Verhalten in jedem mir bekannten C-Standard.
– TLW
26. Juli 2016 um 22:14 Uhr
@TLW Type punning through union ist seit C99 nicht mehr UB. Dies wird zum Beispiel in N1256 6.5.2.3 Fußnote 82 ausdrücklich erwähnt.
– Benutzer694733
13. Februar 2017 um 10:15 Uhr
Auch in C99 erlaubt es explizit eine Trap-Darstellung. Und die Verwendung einer Fallendarstellung ist UB. Ich glaube, es wäre für einen Compiler legal, diesen Code bedingungslos zu kompilieren format_hard_drive(); als Ergebnis.
– TLW
2. Oktober 2022 um 19:33 Uhr
@TLW: Nein, das würde nicht dem C-Standard entsprechen. Wenn Sie den Wert eines Union-Mitglieds verwenden, werden die Bytes als Typ des Mitglieds neu interpretiert. Es kann nicht irgendeinen Wert produzieren; es muss der Wert sein, der durch das Repräsentationsschema für den Typ bestimmt wird. Wenn dies zu einem Trap-Wert führt, ist das Verhalten nicht durch den C-Standard definiert. Aber die int type hat in den meisten aktuellen C-Implementierungen keine Trap-Darstellungen.
– Eric Postpischil
20. Dezember 2022 um 13:09 Uhr
@TLW: Möglicherweise mischen Sie dies mit einem unbestimmter Wert was auftritt, wenn ein Objekt nicht initialisiert wird. Wenn ein Wert unbestimmt ist, kann sich die C-Implementierung so verhalten, als hätte sie einen beliebigen Wert des Typs (bei jeder Verwendung), anstatt den Wert interpretieren zu müssen, der sich in den Bytes ihres Speichers befindet. Aber dennoch muss es ein Wert des Typs sein. Wenn der Typ keine Trap-Werte enthält, kann die Verwendung eines unbestimmten Objekts ohne andere Probleme möglicherweise nicht abgefangen werden.
– Eric Postpischil
20. Dezember 2022 um 13:12 Uhr
Basile Starynkevitch
&x gibt die Adresse von x so hat float* Typ.
(int*)&x wandeln Sie diesen Zeiger in einen Zeiger auf um int dh zu a int* Ding.
*(int*)&x Dereferenzieren Sie diesen Zeiger in ein int Wert. Es wird nicht das tun, was Sie auf Maschinen glauben, wo int und float haben verschiedene Größen.
Sie sagen also, dass der Code nur die Position von x erhält und ausgibt? In diesem Fall würde sich der Wert bei jedem Durchlauf ändern.
– Anonym
9. September 2012 um 21:09 Uhr
Nein, es gibt die Ganzzahl an, die an der Stelle des Floats enthalten ist, also wann sizeof(int) == sizeof[float] es gibt die int derselben Maschinenbitdarstellung wie Ihre x ; nichts wird gedruckt, es sei denn, Sie rufen eine Druckroutine wie auf printf (was nicht in deiner Frage steht)
– Basile Starynkevitch
9. September 2012 um 21:18 Uhr
Ok, es gibt also den an der Stelle im Speicher gespeicherten Wert und wandelt ihn in einen int-Typ um. Wie kann ich das ohne Casting machen?
– Anonym
9. September 2012 um 21:27 Uhr
@BasileStarynkevitch: Was wäre das Problem mit Endianness? Wenn Sie nur die Bits eines Floats auswählen möchten, ist es meiner Meinung nach egal, ob ints Big- oder Little-Endian gespeichert werden.
– Björn Lindqvist
21. Januar 2018 um 16:00 Uhr
Endianness wäre ein Problem, wenn Sie ein Float in ein unsigned int konvertieren würden, wobei Sie die Bits als Flags verwenden und die sendende Funktion/Programm/Gerät nur Floats senden kann.
– Mark Walsh
15. Januar 2019 um 20:37 Uhr
// With the proviso that your compiler implementation uses
// the same number of bytes for an int as for a float:
// example float
float f = 1.234f;
// get address of float, cast as pointer to int, reference
int i = *((int *)&f);
// get address of int, cast as pointer to float, reference
float g = *((float *)&i);
printf("%f %f %08x\n",f,g,i);
Dies fügt den vorherigen Antworten nichts hinzu.
– Matthieu Brucher
23. November 2018 um 14:36 Uhr
Ich stimme nicht zu. Es ist ein nettes eigenständiges Beispiel, Herr Brucher mit einem Ruf von 6.923
– Dino-Dini
23. November 2018 um 15:40 Uhr
Verstößt das nicht gegen die strenge Aliasing-Regel?
– Palapapa
19. November 2022 um 9:25 Uhr
Wildpässer
float x = 43.133;
int y;
assert (sizeof x == sizeof y);
memcpy (&y, &x, sizeof x);
...
Johann Köhler
Sie können den Schwimmer mit einer Referenz werfen. Ein Cast wie dieser sollte niemals Code generieren.
C++
float f = 1.0f;
int i = (int &)f;
printf("Float %f is 0x%08x\n", f, i);
Ausgabe:
Float 1.000000 is 0x3f800000
Wenn Sie einen Cast im C++-Stil wünschen, verwenden Sie einen reinterpret_cast wie diesen.
int i = reinterpret_cast<int &>(f);
Es funktioniert nicht mit Ausdrücken, Sie müssen es in einer Variablen speichern.
int i_times_two;
float f_times_two = f * 2.0f;
i_times_two = (int &)f_times_two;
i_times_two = (int &)(f * 2.0f);
main.cpp:25:13: error: C-style cast from rvalue to reference type 'int &'
Bitte fügen Sie einige Informationen darüber hinzu, wie Ihr Code funktioniert
– Koopkiller
11. August 2015 um 21:01 Uhr
Ihr zweites Beispiel funktioniert, wenn Sie eine rvalue-Referenz verwenden, replace (int&) mit (int&&). Dies ist erforderlich, da der Ausdruck eine Rvalue-Referenz zurückgibt, an die Lvalue-Referenzen nicht gebunden werden können. Ich nehme an, Sie könnten auch verwenden (const int &) an beide binden.
– Nitronoid
19. August 2017 um 10:34 Uhr
Alex Braun
Sie können eine Fließkommazahl nicht (sinnvoll) in eine ‘Ganzzahl’ umwandeln (signed int oder int) auf diese Weise.
Es kann am Ende den Integer-Typ haben, aber es ist eigentlich nur ein Index in den Codierungsraum von IEEE754, kein sinnvoller Wert an sich.
Sie könnten argumentieren, dass ein unsigned int dient einem doppelten Zweck als Bitmuster und als ganzzahliger Wert, aber int nicht.
Es gibt auch Plattformprobleme mit Bit-Manipulation von signierten Ints.
Bitte fügen Sie einige Informationen darüber hinzu, wie Ihr Code funktioniert
– Koopkiller
11. August 2015 um 21:01 Uhr
Ihr zweites Beispiel funktioniert, wenn Sie eine rvalue-Referenz verwenden, replace (int&) mit (int&&). Dies ist erforderlich, da der Ausdruck eine Rvalue-Referenz zurückgibt, an die Lvalue-Referenzen nicht gebunden werden können. Ich nehme an, Sie könnten auch verwenden (const int &) an beide binden.
– Nitronoid
19. August 2017 um 10:34 Uhr
Multiplizieren Sie die Float-Zahl mit dem gewünschten Faktor. In diesem Fall habe ich mit 100.000 multipliziert, weil 5 Dezimalstellen nach Bruch in meiner Operation Bedeutung haben.
Konvertieren Sie es in Bytes und verbinden Sie sie dann und teilen Sie sie erneut durch 100.000.
double angleX, angleY;
angleX = 3.2342;
angleY = 1.34256;
printf("%f, %f", (double)angleX, (double)angleY);
int remain, j;
int TxData[8];
j=0;
remain=0;
unsigned long data = angleX*100000;
printf("\ndata : %d\n", data);
while(data>=256)
{
remain= data%256;
data = data/256;
TxData[j]= remain;
printf("\ntxData %d : %d", j, TxData[j]);
j++;
}
TxData[j] = data;
printf("\ntxData %d : %d", j, TxData[j]);
int i=0;
long int angleSon=0;
for(i=0;i<=j;i++)
{
angleSon += pow(256,i)*TxData[i];
printf("\nangleSon : %li", angleSon);
}
14376400cookie-checkCodierung des int-Werts als IEEE-754-Float (binary32)yes
Dies wirft keinen Float in ein int. Es kopiert nur bitweise ihre Maschinendarstellung, ohne zB zu konvertieren
2.03e1
zu20
[by rounding] als die(int)2.03e1
gegossen wird.– Basile Starynkevitch
9. September 2012 um 20:59 Uhr
Du will tun es bitweise? Nun, so machen Sie es bitweise – es interpretiert nur die Bytes neu. Keine Schritte, wirklich.
– Ry-
♦
9. September 2012 um 21:00 Uhr
Aber 0x7eff8965 = 1325268755 (nach dem Casting). Wenn Sie das HEX in IEEE 754 Calc verwenden, erhalten Sie 1.6983327e+38 und HEX zu Dezimal ergibt: 2130676069 – keines von ihnen gibt das korrekte Ergebnis von 1325268755.
– Anonym
9. September 2012 um 21:04 Uhr
Dieser Code hat ein undefiniertes Verhalten in C. Siehe Abschnitt 6.5 im Standard.
– Paul Hankin
9. September 2012 um 21:30 Uhr
Übrigens konvertiert der von Ihnen gepostete Code ein 32-Bit-Signed-Int in sein 32-Bit-IEEE 754-Single-Precision mit Rundung in Richtung Null. Ich weiß es, weil ich es gestern geschrieben habe.
– Gabe
10. September 2012 um 4:45 Uhr