Frage

Ich versuche, die Bits von einem Schwimmer zu extrahieren, ohne undefiniertes Verhalten aufruft. Hier ist mein erster Versuch:

unsigned foo(float x)
{
    unsigned* u = (unsigned*)&x;
    return *u;
}

Wie ich es verstehe, ist dies nicht wegen der strengen Aliasing Regeln Arbeit garantiert, nicht wahr? Funktioniert es, wenn ein einen Zwischenschritt mit einem Zeichenzeiger nehmen?

unsigned bar(float x)
{
    char* c = (char*)&x;
    unsigned* u = (unsigned*)c;
    return *u;
}

Oder muss ich die einzelnen Bytes selbst extrahieren?

unsigned baz(float x)
{
    unsigned char* c = (unsigned char*)&x;
    return c[0] | c[1] << 8 | c[2] << 16 | c[3] << 24;
}

Natürlich hat dies den Nachteil, je nach Endian, aber ich konnte damit leben.

Die Vereinigung Hack ist definitiv nicht definiertes Verhalten, nicht wahr?

unsigned uni(float x)
{
    union { float f; unsigned u; };
    f = x;
    return u;
}

Nur der Vollständigkeit halber, hier ist eine Referenzversion von foo. Auch nicht definiertes Verhalten, nicht wahr?

unsigned ref(float x)
{
    return (unsigned&)x;
}

So ist es möglich, die Bits von einem Schwimmer zu extrahieren ( unter der Annahme, beide sind 32 Bit breit , natürlich)?


EDIT: Und hier ist die memcpy Version wie von Goz vorgeschlagen. Da viele Compiler noch nicht static_assert unterstützen, habe ich static_assert mit etwas Metaprogrammierung ersetzt:

template <bool, typename T>
struct requirement;

template <typename T>
struct requirement<true, T>
{
    typedef T type;
};

unsigned bits(float x)
{
    requirement<sizeof(unsigned)==sizeof(float), unsigned>::type u;
    memcpy(&u, &x, sizeof u);
    return u;
}
War es hilfreich?

Lösung

über den einzigen Weg, um wirklich alle Probleme zu vermeiden, ist memcpy.

unsigned int FloatToInt( float f )
{
   static_assert( sizeof( float ) == sizeof( unsigned int ), "Sizes must match" );
   unsigned int ret;
   memcpy( &ret, &f, sizeof( float ) );
   return ret;
}

Weil Sie einen festen Betrag sind memcpying der Compiler wird es optimieren aus.

, sagte, dass die Union Methode ist sehr breit unterstützt.

Andere Tipps

  

Die Vereinigung Hack ist definitiv nicht definiertes Verhalten, nicht wahr?

Ja und nein. Gemäß der Norm ist, ist es auf jeden Fall nicht definiertes Verhalten. Aber es ist so eine häufig verwendete Trick, dass GCC und MSVC und soweit ich weiß, jeder andere populäre Compiler, ausdrücklich garantiert, dass es sicher ist und funktioniert wie erwartet.

In der folgenden verstößt nicht gegen die Aliasing-Regel, weil es keinen Gebrauch von lvalues ??Zugriff auf verschiedene Arten überall hat

template<typename B, typename A>
B noalias_cast(A a) { 
  union N { 
    A a; 
    B b; 
    N(A a):a(a) { }
  };
  return N(a).b;
}

unsigned bar(float x) {
  return noalias_cast<unsigned>(x);
}

Wenn Sie wirklich wollen, über die Größe des Schwimmers Typ Agnostiker sein und zurück nur die Roh-Bits, etwas tun, wie folgt aus:

void float_to_bytes(char *buffer, float f) {
    union {
        float x;
        char b[sizeof(float)];
    };

    x = f;
    memcpy(buffer, b, sizeof(float));
}

es dann nennen wie folgt:

float a = 12345.6789;
char buffer[sizeof(float)];

float_to_bytes(buffer, a);

Diese Technik wird natürlich, erzeugt Ausgang speziell auf Ihre Byteanordnung der Maschine.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top