Construyendo un flotador de 32 bits de sus 4 bytes compuestos

Estoy tratando de construir un flotante de 32 bits de sus 4 bytes compuestos. ¿Hay una forma mejor (o más portátil) de hacerlo que con el siguiente método?

#include  typedef unsigned char uchar; float bytesToFloat(uchar b0, uchar b1, uchar b2, uchar b3) { float output; *((uchar*)(&output) + 3) = b0; *((uchar*)(&output) + 2) = b1; *((uchar*)(&output) + 1) = b2; *((uchar*)(&output) + 0) = b3; return output; } int main() { std::cout << bytesToFloat(0x3e, 0xaa, 0xaa, 0xab) << std::endl; // 1.0 / 3.0 std::cout << bytesToFloat(0x7f, 0x7f, 0xff, 0xff) << std::endl; // 3.4028234 × 10^38 (max single precision) return 0; } 

Puede usar un memcpy ( Resultado )

 float f; uchar b[] = {b3, b2, b1, b0}; memcpy(&f, &b, sizeof(f)); return f; 

o una unión * ( Resultado )

 union { float f; uchar b[4]; } u; ub[3] = b0; ub[2] = b1; ub[1] = b2; ub[0] = b3; return uf; 

Pero esto no es más portátil que tu código, ya que no hay garantía de que la plataforma sea poco endian o el float use IEEE binary32 o incluso sizeof(float) == 4 .

(Nota *: Según lo explicado por @James , técnicamente no está permitido en el estándar (C ++ § [clase.unión] / 1) acceder al miembro de la unión uf ).

Las siguientes funciones empaquetan / descomponen bytes que representan un único valor de coma flotante de precisión hacia / desde un búfer en orden de bytes de red. Solo el método de paquete necesita tener en cuenta la endianidad, ya que el método de desempaquetado construye explícitamente el valor de 32 bits de los bytes individuales, desplazándolos por bits en la cantidad apropiada y luego OR-ing juntos. Estas funciones solo son válidas para implementaciones C / C ++ que almacenan un flotante en 32 bits. Esto es cierto para las implementaciones de punto flotante IEEE 754-1985 .

 // unpack method for retrieving data in network byte, // big endian, order (MSB first) // increments index i by the number of bytes unpacked // usage: // int i = 0; // float x = unpackFloat(&buffer[i], &i); // float y = unpackFloat(&buffer[i], &i); // float z = unpackFloat(&buffer[i], &i); float unpackFloat(const void *buf, int *i) { const unsigned char *b = (const unsigned char *)buf; uint32_t temp = 0; *i += 4; temp = ((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]); return *((float *) &temp); } // pack method for storing data in network, // big endian, byte order (MSB first) // returns number of bytes packed // usage: // float x, y, z; // int i = 0; // i += packFloat(&buffer[i], x); // i += packFloat(&buffer[i], y); // i += packFloat(&buffer[i], z); int packFloat(void *buf, float x) { unsigned char *b = (unsigned char *)buf; unsigned char *p = (unsigned char *) &x; #if defined (_M_IX86) || (defined (CPU_FAMILY) && (CPU_FAMILY == I80X86)) b[0] = p[3]; b[1] = p[2]; b[2] = p[1]; b[3] = p[0]; #else b[0] = p[0]; b[1] = p[1]; b[2] = p[2]; b[3] = p[3]; #endif return 4; } 

Puedes usar std::copy :

 float bytesToFloat(uchar b0, uchar b1, uchar b2, uchar b3) { uchar byte_array[] = { b3, b2, b1, b0 }; float result; std::copy(reinterpret_cast(&byte_array[0]), reinterpret_cast(&byte_array[4]), reinterpret_cast(&result)); return result; } 

Esto evita el hack de unión, que técnicamente no está permitido por el lenguaje. También evita el reinterpret_cast(byte_array) comúnmente usado reinterpret_cast(byte_array) , que viola las reglas de aliasing estrictas (se permite reinterpretar cualquier objeto como una matriz de char , por lo que los reinterpret_cast s en esta solución no violan las reglas de alias estrictas) .

Todavía se basa en que float cuatro bytes de ancho y depende de que los cuatro bytes sean un número de coma flotante válido en el formato de punto flotante de su implementación, pero debe hacer esos supuestos o debe escribir un código de manejo especial para realizar la conversión.

No hay forma de hacerlo portátil, ya que diferentes plataformas pueden usar:

  • ordenamiento de bytes diferente (big endian vs. little endian)
  • diferentes representaciones de valores de coma flotante (ver http://en.wikipedia.org/wiki/IEEE_754-1985 para ver un ejemplo)
  • diferentes tamaños para valores de coma flotante

También me pregunto de dónde obtienes estos 4 bytes?

Si asumo que los obtiene de otro sistema y puede garantizar que ambos sistemas usan exactamente el mismo método para almacenar valores de coma flotante en la memoria, puede usar el truco de unión. De lo contrario, se garantiza que su código no será portátil.

Si desea una forma portátil de hacerlo, tendrá que escribir un poco de código para detectar la endialencia del sistema.

 float bytesToFloatA(uchar b0, uchar b1, uchar b2, uchar b3) { float output; *((uchar*)(&output) + 3) = b0; *((uchar*)(&output) + 2) = b1; *((uchar*)(&output) + 1) = b2; *((uchar*)(&output) + 0) = b3; return output; } float bytesToFloatB(uchar b0, uchar b1, uchar b2, uchar b3) { float output; *((uchar*)(&output) + 3) = b3; *((uchar*)(&output) + 2) = b2; *((uchar*)(&output) + 1) = b1; *((uchar*)(&output) + 0) = b0; return output; } float (*correctFunction)(uchar b0, uchar b1, uchar b2, uchar b3) = bytesToFloatA; if ((*correctFunction)(0x3e, 0xaa, 0xaa, 0xab) != 1.f/3.f) // horrifying, I know { correctFunction = bytesToFloatB; }