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.