Función C para convertir flotante a matriz de bytes

8 minutos de lectura

Funcion C para convertir flotante a matriz de bytes
ben bobinado

Estoy tratando de hacer una función que acepte una variable flotante y la convierta en una matriz de bytes. Encontré un fragmento de código que funciona, pero me gustaría reutilizarlo en una función si es posible.

También estoy trabajando con el entorno Arduino, pero entiendo que acepta la mayoría del lenguaje C.

Actualmente funciona:

float_variable = 1.11;
byte bytes_array[4];

*((float *)bytes_array) = float_variable;

¿Qué puedo cambiar aquí para que esta función funcione?

float float_test = 1.11;
byte bytes[4];

// Calling the function
float2Bytes(&bytes,float_test);

// Function
void float2Bytes(byte* bytes_temp[4],float float_variable){ 
     *(float*)bytes_temp = float_variable;  
}

No estoy tan familiarizado con los punteros y demás, pero leí que (flotador) está usando casting o algo así?

¡Cualquier ayuda sería muy apreciada!

Salud

*EDITAR: SOLUCIONADO

Aquí está mi función final que funciona en Arduino para cualquiera que encuentre esto. Hay soluciones más eficientes en las respuestas a continuación, sin embargo, creo que está bien entenderlo.

Función: convierte la variable flotante de entrada en una matriz de bytes

void float2Bytes(float val,byte* bytes_array){
  // Create union of shared memory space
  union {
    float float_variable;
    byte temp_array[4];
  } u;
  // Overite bytes of union with float variable
  u.float_variable = val;
  // Assign bytes to input array
  memcpy(bytes_array, u.temp_array, 4);
}

Llamando a la función

float float_example = 1.11;
byte bytes[4];

float2Bytes(float_example,&bytes[0]);

Gracias por la ayuda de todos, aprendí mucho sobre punteros y referencias en los últimos 20 minutos. ¡Saludos, Stack Overflow!

Funcion C para convertir flotante a matriz de bytes
floris

Lo más fácil es hacer una unión:

#include <stdio.h>

int main(void) {
  int ii;
  union {
    float a;
    unsigned char bytes[4];
  } thing;

  thing.a = 1.234;
  for (ii=0; ii<4; ii++) 
    printf ("byte %d is %02x\n", ii, thing.bytes[ii]);
  return 0;
}

Producción:

byte 0 is b6
byte 1 is f3
byte 2 is 9d
byte 3 is 3f

Nota: no hay garantía sobre el orden de los bytes… depende de la arquitectura de su máquina.

Para que su función funcione, haga esto:

void float2Bytes(byte bytes_temp[4],float float_variable){ 
  union {
    float a;
    unsigned char bytes[4];
  } thing;
  thing.a = float_variable;
  memcpy(bytes_temp, thing.bytes, 4);
}

O para realmente hackearlo:

void float2Bytes(byte bytes_temp[4],float float_variable){ 
  memcpy(bytes_temp, (unsigned char*) (&float_variable), 4);
}

Nota: en cualquier caso, me aseguro de copiar los datos en la ubicación proporcionada como parámetro de entrada. Esto es crucial, ya que las variables locales no existirán después de que regrese (aunque podría declararlas static, pero no te enseñemos malos hábitos. ¿Qué pasa si se vuelve a llamar a la función…)

  • @haccks: la unión de dos elementos de datos de cuatro bytes de tamaño hace que estos dos ocupen la misma memoria física, por lo que cuando escribo en thing.a También escribo en la matriz de bytes. Encontrarás eso &thing.a == &thing.bytes

    – Floris

    25/06/2014 a las 23:50

  • ¡Muchas gracias! Finalmente estoy entendiendo el concepto de uniones.

    – Ben bobinado

    25 de junio de 2014 a las 23:58

  • @haccks: me sorprendió un poco su pregunta y, dada la calidad de sus aportes aquí, en realidad me cuestioné (“¿Hice algo realmente tonto? ¿No debería haber compilado esto?” Incluso encendí -Wstrict-aliasing buscando problemas…). Gracias por el +.

    – Floris

    25 de junio de 2014 a las 23:59

  • +1 por el comentario sobre el orden de los bytes: esto puede explotar si lo usa en la arquitectura incorrecta. Probablemente haya una manera de hacer lo que quieras que no implique depender de endianness…

    –Patrick Collins

    26 de junio de 2014 a las 0:10

  • No, el lugar correcto. Su primer argumento es una matriz de cuatro punteros. Debería ser: cuatro caracteres. (y: no necesita el elenco, memcpy() toma dos punteros vacíos como sus dos primeros argumentos)

    – salvaje

    12 de julio de 2019 a las 11:07

Esta es una manera de hacer lo que quiere que no fallará si está en un sistema con un endianidad del que estás ahora:

byte* floatToByteArray(float f) {
    byte* ret = malloc(4 * sizeof(byte));
    unsigned int asInt = *((int*)&f);

    int i;
    for (i = 0; i < 4; i++) {
        ret[i] = (asInt >> 8 * i) & 0xFF;
    }

    return ret;
}

Puedes verlo en acción aqui: http://ideone.com/umY1bB

El problema con las respuestas anteriores es que se basan en la representación subyacente de floats: C no garantiza que el byte más significativo sea el “primero” en la memoria. El estándar permite que el sistema subyacente implemente floatSin embargo, parece que, si prueba su código en un sistema con un tipo particular de endianness (orden de bytes para tipos numéricos en la memoria), dejará de funcionar dependiendo del tipo de procesador en el que lo esté ejecutando.

Ese es un error realmente desagradable y difícil de corregir y debe evitarlo en la medida de lo posible.

  • Gracias por su contribución, me preocupa que otro sistema pueda malinterpretar una variable flotante. Sin embargo, ¿cómo determina su código de muestra entre sistemas? Perdona mi ignorancia, sigo aprendiendo…

    – Ben bobinado

    26 de junio de 2014 a las 0:55

  • @TylerDurden Funciona en todos los sistemas porque no “rompe” la noción de un flotador con el que está trabajando el sistema. El uso de una unión interactúa directamente con los valores en la memoria, por lo que depende de si el sistema almacena el byte más significativo en el byte de mayor orden del flotante. El uso de esta estrategia solo pasa por otras abstracciones: int y bitshifting, por lo que será consistente sin importar cómo el sistema subyacente ordene sus bytes.

    –Patrick Collins

    26 de junio de 2014 a las 1:00

  • Lo más significativo de un float y un int estará en el mismo lugar independientemente de la endianidad, por lo que el desplazamiento de bits y el enmascaramiento lo extraerán correctamente. Sin embargo, el sistema no garantiza que la parte más significativa de un float es el bit de orden más alto, por lo que puede romperse si usa un union en un sistema de endianness diferente al que desarrollaste.

    –Patrick Collins

    26 de junio de 2014 a las 1:01


  • No tiene que ver con “malinterpretar un flotante”, sino que algunos sistemas representan números como [MOST_SIGNIFICANT_BYTE, 2ND_MOST_SIGNIFICANT, 2ND_LEAST_SIGNIFICANT, LEAST_SIGNIFICANT] y otros los representan como [LEAST_SIGNIFICANT, 2ND_LEAST_SIGNIFICANT, 2ND_MOST_SIGNIFICANT, MOST_SIGNIFICANT].

    –Patrick Collins

    26 de junio de 2014 a las 1:04

  • & 0xFF toma el byte menos significativo, no el primero en la memoria, por lo que evita depender del orden de bytes del sistema subyacente.

    –Patrick Collins

    26 de junio de 2014 a las 2:21

Recomendaría probar una “unión”.

Mira esta publicación:

http://forum.arduino.cc/index.php?topic=158911.0

typedef union I2C_Packet_t{
 sensorData_t sensor;
 byte I2CPacket[sizeof(sensorData_t)];
};

En tu caso, algo como:

union {
  float float_variable;
  char bytes_array[4];
} my_union;

my_union.float_variable = 1.11;

Otra forma más, sin uniones: (Asumiendo byte = char sin firmar)

void floatToByte(byte* bytes, float f){

  int length = sizeof(float);

  for(int i = 0; i < length; i++){
    bytes[i] = ((byte*)&f)[i];
  }

}

1647531669 200 Funcion C para convertir flotante a matriz de bytes
mclaassen

Aunque las otras respuestas muestran cómo lograr esto usando una unión, puede usar esto para implementar la función que desea de esta manera:

byte[] float2Bytes(float val)
{
    my_union *u = malloc(sizeof(my_union));
    u->float_variable = val;
    return u->bytes_array;
}

o

void float2Bytes(byte* bytes_array, float val)
{
    my_union u;
    u.float_variable = val;
    memcpy(bytes_array, u.bytes_array, 4);
}

  • Está devolviendo el puntero a la variable local. Una vez que finaliza el bloque de funciones, u no existirá Puede asignar dinámicamente la unión a su puntero: my_union *u = malloc(sizeof(my_union));.

    – trucos

    25 de junio de 2014 a las 23:54


  • Es un poco más habitual escribir u->float_variable en vez de (*u).float_variable, ¿no es así? ¿Alguna razón en particular por la que elegiste ese camino? Tenga en cuenta también que en la pregunta, la firma de la función pasó el puntero al resultado como un parámetro.

    – Floris

    26 de junio de 2014 a las 0:03


  • Trabajando desde el teléfono. Era demasiado perezoso para encontrar el carácter >. Arreglado ahora.

    – mclaassen

    26 de junio de 2014 a las 0:07


esto parece funcionar también

#include <stddef.h>
#include <stdint.h>
#include <string.h>

float fval = 1.11;
size_t siz;
siz = sizeof(float);

uint8_t ures[siz];

memcpy (&ures, &fval, siz);

luego

float utof;
memcpy (&utof, &ures, siz);

también para doble

double dval = 1.11;
siz = sizeof(double);

uint8_t ures[siz];

memcpy (&ures, &dval, siz);

luego

double utod;
memcpy (&utod, &ures, siz);

  • Está devolviendo el puntero a la variable local. Una vez que finaliza el bloque de funciones, u no existirá Puede asignar dinámicamente la unión a su puntero: my_union *u = malloc(sizeof(my_union));.

    – trucos

    25 de junio de 2014 a las 23:54


  • Es un poco más habitual escribir u->float_variable en vez de (*u).float_variable, ¿no es así? ¿Alguna razón en particular por la que elegiste ese camino? Tenga en cuenta también que en la pregunta, la firma de la función pasó el puntero al resultado como un parámetro.

    – Floris

    26 de junio de 2014 a las 0:03


  • Trabajando desde el teléfono. Era demasiado perezoso para encontrar el carácter >. Arreglado ahora.

    – mclaassen

    26 de junio de 2014 a las 0:07


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad