Codierung des int-Werts als IEEE-754-Float (binary32)

Lesezeit: 7 Minuten

Benutzeravatar von Anonymous
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

Benutzeravatar von Basile Starynkevitch
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.

Und es könnte Endianness-Probleme geben.

Diese Lösung wurde in der verwendet schnelle inverse Quadratwurzel Algorithmus.

  • 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

Benutzeravatar von wildplasser
Wildpässer

float x = 43.133;
int y;

assert (sizeof x == sizeof y);
memcpy (&y, &x, sizeof x);
...

Benutzeravatar von Johan Köhler
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

Benutzeravatar von Alex Brown
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);
}   

1437640cookie-checkCodierung des int-Werts als IEEE-754-Float (binary32)

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy