Question

J'essaie d'extraire les bits d'un float sans invoquer un comportement indéfini.Voilà ma première tentative:

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

Si je comprends bien, cela n'est pas garanti que cela fonctionne en raison de règles strictes d'alias, n'est-ce pas ?Est-ce que ça marche si on fait une étape intermédiaire avec un pointeur de caractère ?

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

Ou dois-je extraire moi-même les octets individuels ?

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

Bien sûr, cela présente l'inconvénient de dépendre du boutisme, mais je pourrais vivre avec ça.

Le hack syndical est définitivement un comportement indéfini, n'est-ce pas ?

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

Juste pour être complet, voici une version de référence de foo.Également un comportement indéfini, non ?

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

Alors, est-il possible d'extraire les bits d'un float (en supposant que les deux ont une largeur de 32 bits, bien sûr)?


MODIFIER:Et voici le memcpy version proposée par Goz.Puisque de nombreux compilateurs ne prennent pas en charge static_assert pourtant, j'ai remplacé static_assert avec quelques modèles de métaprogrammation :

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;
}
Était-ce utile?

La solution

A propos de la seule façon d'éviter vraiment des problèmes est de 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;
}

Parce que vous memcpying un montant fixe le compilateur d'optimiser dehors.

Cela dit la méthode syndicale est très largement pris en charge.

Autres conseils

  

Le hack union est un comportement vraiment non défini, non?

Oui et non. Selon la norme, il est certainement un comportement non défini. Mais il est une astuce utilisée que GCC et MSVC et pour autant que je sache, tous les autres compilateur populaire, garantit explicitement qu'il est sûr et fonctionnera comme prévu.

Ce qui suit ne viole pas la règle d'alias, car il n'a aucune utilité de lvalues ​​pour accéder à différents types n'importe où

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);
}

Si vous voulez vraiment être agnostique sur la taille du flotteur et juste retourner les bits premières, faire quelque chose comme ceci:

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

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

Ensuite, appelez-le comme suit:

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

float_to_bytes(buffer, a);

Cette technique, bien sûr, des produits spécifiques de sortie à l'ordre des octets de votre machine.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top