[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
Schrompf
Moderator
Beiträge: 4852
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

[C++] Bitweises Interpretieren von Floats

Beitrag von Schrompf »

Tach auch. Ich muss hier und da float bitweise bearbeiten. Klingt eigentlich, als wäre es gar kein Problem, ist aber eins. Beispiel:

Code: Alles auswählen

float LiesFloat() { 
  uint32_t bits = LiesBits(32); 
  return reinterpret_cast<float&> (bits); 
}
Funktioniert. Aber unter Linux warnt der GCC
gcc hat geschrieben:../../Traumklassen/TBitLeser.h:100:96: warning: dereferencing type-punned pointer will break strict-aliasing rules (-Wstrict-aliasing)
Ich habe mal ein bisschen nach strict-aliasing gegoogelt und bin der Meinung, dass der GCC da Recht hat. Die Aliasing-Regeln behaupten, dass Zeiger auf verschiedene Datentypen nicht aliasen können, demzufolge bei der Benutzung obigen Codes alle möglichen undefinierten Verhalten auftreten können. Es funktioniert allerdings anscheinend zuverlässig.

Die Frage lautet also: wie greift man verlässlich und warnungsfrei auf die Bits eines float zu?
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8235
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von Krishty »

Erstmal: Unter Visual C++ (und Clang im Kompatibilitätsmodus) geht das, weil Win32 Aliasing erlaubt. Der Workaround gilt also nur für GCC.

AFAIR sollte GCC union gefallen:

Code: Alles auswählen

union FloatAndInt {
  float f;
  uint32_t i;
} fi;
fi.i = LiesBits(32);
return fi.f;
Standardkonform ist das nicht (standardkonform ist nichts außer memcpy()), aber wie gesagt der bevorzugte Weg für GCC.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Spiele Programmierer
Establishment
Beiträge: 426
Registriert: 23.01.2013, 15:55

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von Spiele Programmierer »

Ich habs drüben auf SPPRO ja schon geschrieben.
Im Graugebiert ist das nur in C++, in C ist es aber voll erlaubt.
(http://stackoverflow.com/questions/1137 ... d-behavior)

GCC kennt noch eine weitere Möglichkeit: may_alias

Ich persönlich würde auch die union-Variante verwenden, außer es gibt exotische Gründe die etwas anderes erfordern.
Benutzeravatar
Schrompf
Moderator
Beiträge: 4852
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 »

Jau, auch hier nochmal Danke. Es wird dann wohl die union werden, auch wenn ich die wohl nicht mit irgendwelchen Bequemlichkeits-Methoden ausstatten werde. Speziell der VisualStudio-Compiler ist sehr empfindlich, was sowas angeht, und ich rechne bei diesem Konstrukt mit einigen hundert Millionen Aufrufen. Da werde ich eh irgendwann mal die ASM-Ergebnisse durchgucken müssen.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8235
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von Krishty »

Wenn es so viele sind, vielleicht lieber SSE? Da gibt es dann innerhalb von wenigen Takten in den Registern statt über den Speicher wie in der skalaren Variante … _mm_castsi128_ps dürfte das Zauberintrinsic sein.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4852
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 »

Jau, SSE steht dann auch noch aus.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
FlorianB82
Beiträge: 70
Registriert: 18.11.2010, 05:08
Wohnort: Darmstadt
Kontaktdaten:

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von FlorianB82 »

Moin Schrompf,

ich habe mich mit solchen Themen auch schon zur Genüge auseinandergesetzt - schon alleine, weil ich beruflich zwischen Hardware und Software herumgeistere. Jetzt nur schnell aus dem Gedächtnis - ohne Anspruch auf Topgenauigkeit:

Die meisten aktuellen C++-Compiler führen keine Optimierungen aufgrund der Strict-Aliasing-Rules durch - da sollte also trotz der Warnung kein unerwartetes Verhalten aufgrund Optimierung auftreten. Soweit ich aber weiß, führen neuere GCC-Versionen solche Optimierungen durch - hier könntest du dann gegebenenfalls interessante Effekte beobachten.

Wenn du die Warnung vernünftig losbekommen willst - was ich nur begrüßen kann, da ich ein großer Freund von Warning-Free-Code bin - gibt es immer die Möglichkeit, über einen char-Pointer (signed oder unsigned) zu casten. Gemäß der Strict-Aliasing-Rules dürfen (unsigned/signed) char*-Pointer nämlich jeden beliebigen Typ aliasen, und du bist damit safe und standardkonform.

Über einen Union zu gehen ist eigentlich auch Murks. Interessant, dass der Compiler da nicht meckert. Wenn mich nicht alles täuscht, erlaubt der C++-Standard nur das Lesen aus einem Feld eines Unions, in das man vorher auch geschrieben hat - alles andere ist UB. Da du das aber genau machen würdest (in i schreiben und aus f lesen), kannste dir das auch sparen. Ist auch nicht besser oder schlechter als der brutalo-cast.
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 »

FlorianB82 hat geschrieben:Die meisten aktuellen C++-Compiler führen keine Optimierungen aufgrund der Strict-Aliasing-Rules durch - da sollte also trotz der Warnung kein unerwartetes Verhalten aufgrund Optimierung auftreten. Soweit ich aber weiß, führen neuere GCC-Versionen solche Optimierungen durch - hier könntest du dann gegebenenfalls interessante Effekte beobachten.
Da irrst du dich gewaltig! Die Strict-Aliasing-Rule ist Grundnahrungsmittel jedes auch nur annähernd brauchbaren Compilers und für die Performance von C++ von essentieller Bedeutung. Ohne die müsste der Compiler davon ausgehen, dass jede Modifikation eines Objektes potentiell den Wert jedes anderen Objektes verändert, was bedeuten würde, dass der Compiler nach jeder Modifikation irgendeines Objektes die Werte aller anderen Objekte neu aus dem Speicher laden muss, bevor er sie wieder verwenden kann. Oft ist selbst die Strict-Aliasing-Rule nicht genug, genau darum bietet so ziemlich jeder Compiler eine Variante des restrict Keywords als Extension an...

Nur mal so als kleine Illustration:

Code: Alles auswählen

int blub(int* i, float* f)
{
  *i = 10;
  *f = 32.0f;
  return *i + *f;
}

int main()
{
  union
  {
    float f;
    int i;
  } x;
  
  return blub(&x.i, &x.f);
}
Jeder gängige Compiler wird dir das – dank Strict-Aliasing-Rule – zu einem return 42; optimieren. Ändern wir den int zu char, sieht die Sache plötzlich völlig anders aus...

FlorianB82 hat geschrieben:Wenn du die Warnung vernünftig losbekommen willst - was ich nur begrüßen kann, da ich ein großer Freund von Warning-Free-Code bin - gibt es immer die Möglichkeit, über einen char-Pointer (signed oder unsigned) zu casten. Gemäß der Strict-Aliasing-Rules dürfen (unsigned/signed) char*-Pointer nämlich jeden beliebigen Typ aliasen, und du bist damit safe und standardkonform.
Die Frage ist nur, was du mit diesem char* dann tun willst (signed char wäre übrigens bereits ab hier schon wieder UB, nur char und unsigned char dürfen aliasen). Willst du auf irgendein anderes als das erste Byte der Objektrepräsentation, auf die dein char* zeigt, zugreifen, wirst du Pointerarithmetik betreiben müssen und spätestens dann haben wir UB, denn Pointerarithmetik auf Pointern, die nicht in ein Array oder auf das letzte Element eines Arrays zeigen, ist UB... ;)
FlorianB82 hat geschrieben:Über einen Union zu gehen ist eigentlich auch Murks. Interessant, dass der Compiler da nicht meckert. Wenn mich nicht alles täuscht, erlaubt der C++-Standard nur das Lesen aus einem Feld eines Unions, in das man vorher auch geschrieben hat - alles andere ist UB. Da du das aber genau machen würdest (in i schreiben und aus f lesen), kannste dir das auch sparen. Ist auch nicht besser oder schlechter als der brutalo-cast.
In der Tat; das funktioniert leider alles nicht...
Benutzeravatar
Schrompf
Moderator
Beiträge: 4852
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 »

Hm. Klingt für mich, als wäre ich im UB, egal was ich tue. Ich habe hier aktuell die union-Lösung implementiert, die zumindest auf dem GCC warnungsfrei kompiliert und das tut, was sie soll. Ein Hoch auf Unittests. Ob und wie sich Visual Studio damit schlägt, werde ich jetzt aber so schnell nicht herausfinden. Und den entstehenden ASM-Code werde ich mir am Ende eh zu Gemüte führen müssen, da das Manöver u.A. Teil meines Voxelkrams ist, der potentiell Gigabytes davon durchackert.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8235
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von Krishty »

dot hat geschrieben:Die Strict-Aliasing-Rule ist Grundnahrungsmittel jedes auch nur annähernd brauchbaren Compilers und für die Performance von C++ von essentieller Bedeutung.
Die Strict Aliasing Rule ist nur für GCC von essentieller Bedeutung. Und sie ist grundsätzlich falsch (weil C++ das Re-Instanzieren einer Variable am alten Platz mit neuem Typ erlaubt) und die GCC-Leute haben, als sie das gemerkt haben, einfach beschlossen, sie doch drin zu lassen weil sie in 99 % der Fälle halt funktioniert (indem sie den User sinnlos nervt). Ich sehe das als bloße Schikane.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8235
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von Krishty »

Noch kurz an Schrompf:
  1. Kapsle das in einer Funktion – bloß nicht an jeder Stelle, an der du ein float liest, diesen union-Kack und die enstprechenden #if __GCC__ reproduzieren (machst du sowieso nicht, aber hier lesen ja noch andere mit)
  2. Diese Funktion sollte auf keinen Fall Referenz oder Zeiger zurückgeben, sondern unbedingt eine neue Instanz – sonst produzieren viele Compiler tatsächlich jede Menge nutzlose Loads und Stores, sobald du mit dem Wert arbeitest
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 »

Krishty hat geschrieben:
dot hat geschrieben:Die Strict-Aliasing-Rule ist Grundnahrungsmittel jedes auch nur annähernd brauchbaren Compilers und für die Performance von C++ von essentieller Bedeutung.
Die Strict Aliasing Rule ist nur für GCC von essentieller Bedeutung. Und sie ist grundsätzlich falsch (weil C++ das Re-Instanzieren einer Variable am alten Platz mit neuem Typ erlaubt) und die GCC-Leute haben, als sie das gemerkt haben, einfach beschlossen, sie doch drin zu lassen weil sie in 99 % der Fälle halt funktioniert (indem sie den User sinnlos nervt). Ich sehe das als bloße Schikane.
Ok, offenbar verstehen wir unter der Strict-Aliasing-Rule was anderes!? Ich verstehe drunter die Regel, die besagt, dass es UB ist, auf das stored value eines Objektes über etwas anderes als ein dem dynamischen Typ des Objektes entsprechendes glvalue oder ein glvalue vom Typ char oder unsigned char zuzugreifen (die restlichen Ausnahmen seien hier mal egal). Diese Regel ist definitiv nicht "falsch" und von MSVC über clang bis zu nvcc macht jeder Compiler, mit dem ich je zu tun hatte, ständig davon Gebrauch...
Schrompf hat geschrieben:Hm. Klingt für mich, als wäre ich im UB, egal was ich tue. Ich habe hier aktuell die union-Lösung implementiert, die zumindest auf dem GCC warnungsfrei kompiliert und das tut, was sie soll. Ein Hoch auf Unittests. Ob und wie sich Visual Studio damit schlägt, werde ich jetzt aber so schnell nicht herausfinden. Und den entstehenden ASM-Code werde ich mir am Ende eh zu Gemüte führen müssen, da das Manöver u.A. Teil meines Voxelkrams ist, der potentiell Gigabytes davon durchackert.
Wie gesagt: memcpy() in ein char Array ist die einzige, mir bekannte Vorgehensweise, die vermutlich eher kein UB als UB ist. In der Praxis wird der Type-Punning-per-union-Hack funktionieren, weil praktisch jeder Compiler ihn supported. Es ist aber natürlich dennoch UB...
Benutzeravatar
Krishty
Establishment
Beiträge: 8235
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:
dot hat geschrieben:Die Strict-Aliasing-Rule ist Grundnahrungsmittel jedes auch nur annähernd brauchbaren Compilers und für die Performance von C++ von essentieller Bedeutung.
Die Strict Aliasing Rule ist nur für GCC von essentieller Bedeutung. Und sie ist grundsätzlich falsch (weil C++ das Re-Instanzieren einer Variable am alten Platz mit neuem Typ erlaubt) und die GCC-Leute haben, als sie das gemerkt haben, einfach beschlossen, sie doch drin zu lassen weil sie in 99 % der Fälle halt funktioniert (indem sie den User sinnlos nervt). Ich sehe das als bloße Schikane.
Ok, offenbar verstehen wir unter der Strict-Aliasing-Rule was anderes!? Ich verstehe drunter die Regel, die besagt, dass es UB ist, auf das stored value eines Objektes über etwas anderes als ein dem dynamischen Typ des Objektes entsprechendes glvalue oder ein glvalue vom Typ char oder unsigned char zuzugreifen (die restlichen Ausnahmen seien hier mal egal).
Stimmt; ich habe sie glatt mit Type-Based Aliasing Analysis verwechselt :( Sorry! Der Compiler macht von der Strict Aliasing Rule aber fast keinen Gebrauch, sondern nur von der eventuell abgeleiteten Type-Based Aliasing Analysis (und die ist im Arsch, weil Dynamic Types ein Analyse-Horror sind). MSVC sowieso nicht, weil die Win32-Garantien dort Vorrang gegenüber C++-Regeln haben (und da darf man eben casten, so viel man will).
dot hat geschrieben:Wie gesagt: memcpy() in ein char Array ist die einzige, mir bekannte Vorgehensweise, die vermutlich eher kein UB als UB ist.
Richtig. Seit ein paar Jahren könnten Compiler auch clever genug sein, das zu optimalem Code zu optimieren (cbloom hat da was erwähnt11-14-15 | Copying words). Wenn du's ausprobierst, poste bitte mal das Disassembly mit VC 2015 und GCC.
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 »

Krishty hat geschrieben:
dot hat geschrieben:Wie gesagt: memcpy() in ein char Array ist die einzige, mir bekannte Vorgehensweise, die vermutlich eher kein UB als UB ist.
Richtig. Seit ein paar Jahren könnten Compiler auch clever genug sein, das zu optimalem Code zu optimieren (cbloom hat da was erwähnt11-14-15 | Copying words). Wenn du's ausprobierst, poste bitte mal das Disassembly mit VC 2015 und GCC.
Wie hier schon erwähnt, kompilieren gcc, clang, MSVC und icc ein solches memcpy() zu einem einfachen move... :)

Beispiel zum selbst ausprobieren:

Code: Alles auswählen

#include <new>
#include <cstring>

unsigned int float_as_int(float f)
{
  unsigned char b[4];
  //unsigned int x;

  std::memcpy(&b, &f, sizeof(b));
  //std::memcpy(&x, &f, sizeof(x));
  //new (b) float(f);

  return (b[0] << 0) | (b[1] << 8) | (b[2] << 16) | (b[3] << 24);
  //return x;
}

int main()
{
  return float_as_int(42.0f);
}
Leider ist MSVC nicht schlau genug um das bitweise Zusammenbauen zu einem int zu erkennen. gcc und clang machen wirklich nur ein move aus der ganzen Funktion...

@Schrompf: Da oben findest du auch noch eine bisher nicht erwähnte, alternative Variante zu Type-punning über placement new. Ist leider aber auch UB... ;)


Edit: Ok, wenn man es so schreibt:

Code: Alles auswählen

((b[0] << 0) & 0xFF) | ((b[1] << 8) & 0xFF) | ((b[2] << 16) & 0xFF) | ((b[3] << 24) & 0xFF)
dann macht auch MSVC nur ein move aus dem ganzen Ding... :)
Benutzeravatar
Krishty
Establishment
Beiträge: 8235
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Bitweises Interpretieren von Floats

Beitrag von Krishty »

Danke, der Thread war noch nicht verlinkt.
Krishty hat geschrieben:Der Beispielcode ist für den umgekehrten Fall, float zu int. Wie würde int zu float aussehen? Danach können wir zu den einzelnen Anweisungen kommen :)
Okay; so wie ich das sehe erzeugen Clang und GCC für alle Möglichkeiten (Placement new, union, memcpy, reinterpret_cast) den gleichen Code. Sehr schön.

Ich setze mal SSE2 voraus weil ich weiß, dass Schrompf das benutzt: -O3 -msse2

Da wäre das erstrebenswerte Ergebnis ein einzelnes MOVD der Ganzzahl in ein SSE-Register (Register zu Register, ohne Memory Roundtrip). Clang bekommt das hin:

  int_as_float(unsigned int):
    movd xmm0, edi
    ret


GCC nicht:

  int_as_float(unsigned int):
    mov DWORD PTR [rsp-4], edi
    movss xmm0, DWORD PTR [rsp-4]
    ret


Visual C++? Keine Ahnung. Als ich das letzte Mal versuchte habe, MOVD aus dem herauszukitzeln, hatte ich einen Blackout und jemand muss währenddessen mit Fäkalien SSE-Intrinsics auf meine Wände geschmiert haben.
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 »

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: 4852
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: 4852
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: 2110
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: 8235
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: 8235
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: 4852
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