bits flotantes y aliasing estricto

Intento extraer los bits de un flotante sin invocar un comportamiento indefinido. Aqui esta mi primer bash:

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

Según tengo entendido, no está garantizado que funcione debido a las estrictas reglas de alias, ¿verdad? ¿Funciona si se toma un paso intermedio con un puntero de personaje?

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

¿O debo extraer los bytes individuales yo 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 la desventaja de depender de endianness, pero podría vivir con eso.

El truco sindical es definitivamente un comportamiento indefinido, ¿verdad?

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

Solo para completar, aquí hay una versión de referencia de foo . También comportamiento indefinido, ¿verdad?

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

Entonces, ¿es posible extraer los bits de un flotador ( asumiendo que ambos son de 32 bits de ancho , por supuesto)?


EDITAR: Y aquí está la versión memcpy propuesta por Goz. Como muchos comstackdores aún no soportan static_assert , he reemplazado static_assert con alguna metaprogtwigción de plantillas:

 template  struct requirement; template  struct requirement { typedef T type; }; unsigned bits(float x) { requirement::type u; memcpy(&u, &x, sizeof u); return u; } 

La única manera de evitar realmente cualquier problema es 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; } 

Debido a que está memcpying una cantidad fija, el comstackdor lo optimizará.

Dicho esto, el método de unión es MUY ampliamente compatible.

El truco sindical es definitivamente un comportamiento indefinido, ¿verdad?

Si y no. De acuerdo con el estándar, definitivamente es un comportamiento indefinido. Pero es un truco tan utilizado que GCC y MSVC y, hasta donde yo sé, cualquier otro comstackdor popular, explícitamente garantiza que es seguro y funcionará como se espera.

Lo siguiente no viola la regla de aliasing, porque no tiene uso de valores l que acceden a diferentes tipos en cualquier lugar

 template 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(x); } 

Si realmente quieres ser agnóstico sobre el tamaño del tipo de letra flotante y simplemente devolver los bits crudos, haz 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)); } 

Entonces llámalo así:

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

Esta técnica, por supuesto, producirá un resultado específico para el pedido de bytes de su máquina.