Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Schrompf

Alter Hase

  • »Schrompf« ist der Autor dieses Themas

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

1

07.12.2015, 16:39

[C++] Bitweises Interpretieren von Floats

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

C-/C++-Quelltext

1
2
3
4
float LiesFloat() { 
  uint32_t bits = LiesBits(32); 
  return reinterpret_cast<float&> (bits); 
}


Funktioniert. Aber unter Linux warnt der GCC

Zitat von »"gcc"«

../../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?
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

2

07.12.2015, 16:47

Mit memcpy in einen uint32_t kopieren?

Nimelrian

Alter Hase

Beiträge: 1 216

Beruf: Softwareentwickler (aktuell Web/Node); Freiberuflicher Google Proxy

  • Private Nachricht senden

3

07.12.2015, 16:49

Magic! Kompiliert laut cpp.sh ohne Warnungen :)

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
union Float_t
{
    Float_t(float num = 0.0f) : f(num) {}
    // Portable extraction of components.
    bool Negative() const { return (i >> 31) != 0; }
    int32_t RawMantissa() const { return i & ((1 << 23) - 1); }
    int32_t RawExponent() const { return (i >> 23) & 0xFF; }
 
    int32_t i;
    float f;
#ifdef _DEBUG
    struct
    {   // Bitfields for exploration. Do not use in production code.
        uint32_t mantissa : 23;
        uint32_t exponent : 8;
        uint32_t sign : 1;
    } parts;
#endif
};
Ich bin kein UserSideGoogleProxy. Und nein, dieses Forum ist kein UserSideGoogleProxyAbstractFactorySingleton.

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

4

07.12.2015, 16:55

Es gibt auch noch eine weitere Lösung: may_alias
Wenn du allerdings mit einem schlichten union arbeiten kannst, empfehle ich das.
Das entspricht dem C Sprachstandard ohne auf einen Funktionsaufruf in die StdLib zurückgreifen zu müssen.
Als ich zuletzt damit gearbeitet habe, wurde so auch der beste Code generiert.

Schrompf

Alter Hase

  • »Schrompf« ist der Autor dieses Themas

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

5

07.12.2015, 16:58

Danke! memcpy() wollte ich natürlich vermeiden, weil der Compiler dann gar keine Chance mehr hat, das Load&Store zu vermeiden. Ich habe inzwischen eine Weile weitergegoogelt und sah dort verschiedene Vorschläge, die sich mit Nimelrians "union"-Vorschlag decken. Je schlichter die union, desto besser die Chance, dass der Compiler das Manöver erkennt und rein arithmetischen Budenzauber in den Registern ausspuckt. Das wäre dann wohl auch ein Argument dagegen, sich irgendne Funky Template-Lösung auszudenken, die den passenden Integer-Typ zum Datentyp automatisch wählt.

Bei ner x86-CPU ist aber zumindest das Umladen von FP-Registern in Int-Register unvermeidlich, soweit ich das sehen kann.

[Edit] Im IRC geht gerade die Diskussion los, dass man in einer Union nur auf das Element zugreifen darf, was man zuletzt geschrieben hat. Alles andere wäre ebenso undefiniertes Verhalten. Hm. Es bleibt weiter spannend.
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

6

07.12.2015, 16:59

Das ist afaik undefiniertes Verhalten (per Union), weil man nur von dem Member lesen darf, der zuletzt geschrieben wurde.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Nimelrian

Alter Hase

Beiträge: 1 216

Beruf: Softwareentwickler (aktuell Web/Node); Freiberuflicher Google Proxy

  • Private Nachricht senden

7

07.12.2015, 17:08

Chat hat sich jetzt geeinigt auf: Wenn Standardkonform: memcpy. Ansonsten union.
Ich bin kein UserSideGoogleProxy. Und nein, dieses Forum ist kein UserSideGoogleProxyAbstractFactorySingleton.

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

8

07.12.2015, 17:09

Wenn man das streng sieht, ist es in C++ leider immernoch so.
In C stimmt das nicht mehr - in so fern würde ich, zusammen mit der Tatsache dass es sich um eine extrem verbreitete Vorgehensweise handelt, das in dem Fall das nicht so eng sehen.
http://stackoverflow.com/questions/11373…efined-behavior

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

9

08.12.2015, 10:41

Die Frage lautet also: wie greift man verlässlich und warnungsfrei auf die Bits eines float zu?

tl;dr: In C++ leider gar nicht... :(

Tach auch. Ich muss hier und da float bitweise bearbeiten.

Nur mal rein prinzipiell: Was genau musst du denn in den Bits bearbeiten?

Danke! memcpy() wollte ich natürlich vermeiden, weil der Compiler dann gar keine Chance mehr hat, das Load&Store zu vermeiden.

Du unterschätzt deinen Compiler. Wenn du's mal ausprobierst, wirst du feststellen, dass das memcpy() zu einem simplen move kompiliert... ;)

[Edit] Im IRC geht gerade die Diskussion los, dass man in einer Union nur auf das Element zugreifen darf, was man zuletzt geschrieben hat. Alles andere wäre ebenso undefiniertes Verhalten. Hm. Es bleibt weiter spannend.

Type punning ist generell UB. Per union sowieso und memcpy() in einen uint32_t wäre imo ebenfalls UB (). Die einzige Vorgehensweise, die man imo als laut Standard potentiell mehr erlaubt als verboten ansehen kann, ist, die Bytes eines Objektes von trivially copyable Type per memcpy() in ein char oder unsigned char Array zu kopieren und dann die einzelnen chars im Array anzuschauen. Aber selbst da begibt man sich imo in eine Grauzone (der Standard sagt imo streng genommen nur, dass man die Bytes dann aus dem Array weiter in ein Objekt des selben Typs kopieren kann und dieses dann den selben Wert wie das ursrüngliche Objekt hat. Dass man dazwischen die Elemente des Arrays anschaut, ist lediglich nicht explizit verboten)...

Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von »dot« (08.12.2015, 11:13)


Schrompf

Alter Hase

  • »Schrompf« ist der Autor dieses Themas

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

10

08.12.2015, 11:00

Danke nochmal für den Input. Ich brauche das hier, weil ich einen maximal komprimierten Bitstream habe, in dem aber halt gelegentlich auch unkomprimierte floats auftauchen. Die sind dann zumeist nicht auf Byte-Grenzen aligned, geschweige denn auf float. Ich habe das gleiche Problem aber auch in der float16-Klasse, die aus float bitweise erstellt und in floats zurückkonvertiert werden kann. Und ich habe das Problem bei der Endian-Konvertierung von float und double, weil ich (zugegebenermaßen aus antiken Kompatibilitätsgründen) die meisten Binärdaten in BigEndian speichere.
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

Werbeanzeige