Pregunta

Estoy intentando extraer los bits de un flotador sin invocar un comportamiento indefinido. Aquí está mi primer intento:

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

A mi entender, esto no se garantiza que funcione debido a las reglas de alias estricto, ¿verdad? ¿Funciona si un tomar un paso intermedio con un puntero personaje?

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

O tengo para extraer los bytes individuales a mí mismo?

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

Por supuesto, esto tiene el inconveniente de depender de la orden de bits, pero yo podría vivir con eso.

El truco unión es sin duda un comportamiento indefinido, ¿verdad?

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

Sólo por completo, aquí hay una versión de referencia del foo. También comportamiento indefinido, ¿verdad?

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

lo tanto, es posible extraer los bits de un flotador ( asumiendo ambos son de 32 bits de ancho , por supuesto)?


EDIT: Y aquí es la versión memcpy propuesto por Goz. Dado que muchos compiladores no admiten static_assert sin embargo, he sustituido static_assert con un poco de metaprogramming plantilla:

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;
}
¿Fue útil?

Solución

Sobre la única manera de evitar cualquier problema de verdad es a establecimiento de memoria.

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

Debido a que está memcpying una cantidad fija al compilador optimizará a cabo.

Dicho esto el método de unión es muy amplio apoyo.

Otros consejos

  

El truco unión es sin duda un comportamiento indefinido, ¿verdad?

Sí y no. Según la norma, es sin duda un comportamiento indefinido. Pero es como un truco de uso común que GCC y MSVC y por lo que yo sé, todos los demás compilador popular, garantiza explícitamente que es seguro y funciona como se esperaba.

La siguiente no se viola la regla de aliasing, ya que no tiene uso de lvalues ??Acceso a diferentes tipos en cualquier lugar

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 realmente quiere ser agnósticos sobre el tamaño del tipo de flotador y sólo devuelve los bits primas, hacer algo como esto:

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

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

A continuación, llamarlo así:

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

float_to_bytes(buffer, a);

Esta técnica, por supuesto, la producción de productos específicos de orden de bytes de la máquina.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top