[C++] Bitweises Interpretieren von Floats

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von dot »

Code: Alles auswählen

#include <cstdint>
#include <limits>
#include <cstring>

std::uint32_t float_as_int(float f)
{
  static_assert(sizeof(float) == 4);
  static_assert(std::numeric_limits<float>::is_iec559);
  
  unsigned char b[4];
  std::memcpy(&b, &f, sizeof(b));

  return ((b[0] <<  0U) & 0xFFU) |
         ((b[1] <<  8U) & 0xFF00U) |
         ((b[2] << 16U) & 0xFF0000U) |
         ((b[3] << 24U) & 0xFF000000U);
}

float int_as_float(std::uint32_t i)
{
  static_assert(sizeof(float) == 4);
  static_assert(std::numeric_limits<float>::is_iec559);
  
  unsigned char b[4] = {
    static_cast<unsigned char>((i & 0xFFU) >>  0),
    static_cast<unsigned char>((i & 0xFF00U) >>  8),
    static_cast<unsigned char>((i & 0xFF0000U) >> 16),
    static_cast<unsigned char>((i & 0xFF000000U) >> 24)
  };
  
  float f;
  std::memcpy(&f, b, sizeof(b));
  
  return f;
}

int main()
{
  return int_as_float(float_as_int(42.0f));
}
MSVC bekommt es leider gar nicht hin (vergesst den Edit von vorhin, der Code war falsch), icc auch nicht, gcc nur für eine der beiden Funktionen. clang generiert für alles perfekten Code:

Code: Alles auswählen

float_as_int(float):
        movd    eax, xmm0
        ret

int_as_float(unsigned int):
        movd    xmm0, edi
        ret

main:
        mov     eax, 42
        ret
I'm seriously impressed :o
Zuletzt geändert von dot am 09.12.2015, 15:10, insgesamt 6-mal geändert.
Benutzeravatar
xq
Establishment
Beiträge: 1581
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von xq »

Wow, das ist mal wirklich gute Optimierung! Schöne Sache :)
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Benutzeravatar
Schrompf
Moderator
Beiträge: 4855
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von Schrompf »

Sorry, den sppro-Thread hätte ich auch selbst verlinken sollen. Da kam auch einiges Gutes bei rum. Ich freue mich auch hier sehr, so viel Input zu diesem nur scheinbar simplen Problem zu bekommen. Nach Allem bin ich jetzt aber *noch* verwirrter, wie ich das Problem nun eigentlich lösen soll. Mir wird wohl nichts anderes übrigbleiben, als mal alle Möglichkeiten aufzustellen und durch alle drei bei mir benutzten Compiler zu scheuchen: VC, GCC, CLANG. ICC ist ein interessanter Gedanke, aber für mich aktuell nicht relevant.

Ich werde für den Voxel-Kram sogar bis SSE4 hoch voraussetzen, weil irgendwo da erst irgendwelche Bit-Operationen dazukommen, mit denen ich einige Voxelklumpen-Operationen schneller hinbekomme.

[Edit] Der Vollständigkeit wegen: der Thread drüben im sppro findet sich unter https://www.spieleprogrammierer.de/18-c ... on-floats/
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von dot »

Folgende Version ist (sagen wir mal) Endian-independent, imo einigermaßen standardkonform und erzeugt unter MSVC, clang und gcc den besten Code, den ich dem jeweiligen Compiler bisher entlocken konnte (icc schneidet interessanterweise am schlechtesten ab, wobei Version 13 wohl nicht die neueste ist):

Code: Alles auswählen

#include <cstdint>
#include <limits>
#include <cstring>

std::uint32_t float_as_int(float f)
{
  static_assert(sizeof(float) == sizeof(std::uint32_t), "float not 32 bits wide");
  static_assert(std::numeric_limits<float>::is_iec559, "float not IEEE 754 compliant");
  
  std::uint32_t i;
  std::memcpy(&i, &f, sizeof(std::uint32_t));

  return i;
}

float int_as_float(std::uint32_t i)
{
  static_assert(sizeof(float) == sizeof(std::uint32_t), "float not 32 bits wide");
  static_assert(std::numeric_limits<float>::is_iec559, "float not IEEE 754 compliant");
  
  float f;
  std::memcpy(&f, &i, sizeof(std::uint32_t));
  
  return f;
}

int main()
{
  return int_as_float(float_as_int(42.0f));
}
Bin immer noch extrem impressed vom Code, den clang generiert... :o

Man sollte erwähnen, dass der union-Hack unter allen Compilern den selben Code erzeugt (icc kommt mit union besser zurecht)...
Zuletzt geändert von dot am 09.12.2015, 16:28, insgesamt 1-mal geändert.
Benutzeravatar
Schrompf
Moderator
Beiträge: 4855
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von Schrompf »

Cool, Danke für Deine Zeit! Ich will das zwar alles mal durchprobieren, aber ich komme dazu frühestens im neuen Jahr. Und dann stehen die Chancen gut, dass ich mich gar nicht mehr daran erinnere :-)
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von dot »

Ich kann zum schnellen Rumprobieren nur nochmal meine Empfehlung wiederholen: http://gcc.godbolt.org/ ;)
Alexander Kornrumpf
Moderator
Beiträge: 2113
Registriert: 25.02.2009, 13:37

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von Alexander Kornrumpf »

dot hat geschrieben:imo einigermaßen standardkonform
Nur mal so aus Neugier, es muss doch einen bekannten standardkonformen, schlimmstenfalls langsamen Weg geben, an die Bits von beliebigem zu kommen. Im Zweifelsfall über einen Stream?!
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von dot »

Alexander Kornrumpf hat geschrieben:
dot hat geschrieben:imo einigermaßen standardkonform
Nur mal so aus Neugier, es muss doch einen bekannten standardkonformen, schlimmstenfalls langsamen Weg geben, an die Bits von beliebigem zu kommen. Im Zweifelsfall über einen Stream?!
Kopieren per std::memcpy() in ein char Array ist der einzige, mir bekannte, arguably wirklich standardkonforme Weg (und der funktioniert nur, so lange es sich um einen trivially-copyable Type handelt).
Benutzeravatar
Biolunar
Establishment
Beiträge: 154
Registriert: 27.06.2005, 17:42
Alter Benutzername: dLoB

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von Biolunar »

dot hat geschrieben:
Alexander Kornrumpf hat geschrieben:
dot hat geschrieben:imo einigermaßen standardkonform
Nur mal so aus Neugier, es muss doch einen bekannten standardkonformen, schlimmstenfalls langsamen Weg geben, an die Bits von beliebigem zu kommen. Im Zweifelsfall über einen Stream?!
Kopieren per std::memcpy() in ein char Array ist der einzige, mir bekannte, arguably wirklich standardkonforme Weg (und der funktioniert nur, so lange es sich um einen trivially-copyable Type handelt).
In C99 und C11 ist das Konvertieren per union ebenfalls standardkonform (in C89 nicht!). Im Standard steht auch was von Zugriff über einen „character type“, ich nehme daher an, dass der Zugriff über char, signed char und unsigned char Pointer wohldefiniert ist.
Benutzeravatar
Krishty
Establishment
Beiträge: 8240
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von Krishty »

Mach noch ein static_assert() mit alignof!
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von dot »

Biolunar hat geschrieben:
dot hat geschrieben:
Alexander Kornrumpf hat geschrieben:
dot hat geschrieben:imo einigermaßen standardkonform
Nur mal so aus Neugier, es muss doch einen bekannten standardkonformen, schlimmstenfalls langsamen Weg geben, an die Bits von beliebigem zu kommen. Im Zweifelsfall über einen Stream?!
Kopieren per std::memcpy() in ein char Array ist der einzige, mir bekannte, arguably wirklich standardkonforme Weg (und der funktioniert nur, so lange es sich um einen trivially-copyable Type handelt).
In C99 und C11 ist das Konvertieren per union ebenfalls standardkonform (in C89 nicht!). Im Standard steht auch was von Zugriff über einen „character type“, ich nehme daher an, dass der Zugriff über char, signed char und unsigned char Pointer wohldefiniert ist.
Ja, in C11 ist der Zugriff über char Pointer wohldefiniert (offenbar sogar für signed char). In C++ ist das dagegen alles UB.
Krishty hat geschrieben:Mach noch ein static_assert() mit alignof!
Sollte imo nicht nötig sein, da wir ja kopieren... ;)
Benutzeravatar
Krishty
Establishment
Beiträge: 8240
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von Krishty »

dot hat geschrieben:
Krishty hat geschrieben:Mach noch ein static_assert() mit alignof!
Sollte imo nicht nötig sein, da wir ja kopieren... ;)
Ach stimmt; klar :)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4855
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von Schrompf »

Moment mal. Soll das bedeuten, dass ein Umweg über char* ok ist:

Code: Alles auswählen

float f = *(reinterpret_cast<const float*> (reinterpret_cast<const char*> (&i)));
Aber mein ursprünglicher Weg ohne die Zwischenstufe nicht?

Code: Alles auswählen

float f = reinterpret_cast<float&> (i);
Ernsthaft?
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von dot »

Nein, ist beides gleichermaßen UB, auch in C.

Abgesehen davon, sollte man wohl erwähnen, dass Type-Punning per union in C11 an sich zwar kein UB sein mag, der C11 Standard aber auch nicht sagt, dass das Ganze dann nicht crashen darf, wenn der uminterpretierte Bitpattern dummerweise gerade kein gültiger Wert des jeweiligen Typs ist (die entsprechende Fußnote weist sogar explizit darauf hin), was die Nützlichkeit des Ganzen dann doch wieder stark relativiert...
Antworten