C-Funktion zum Konvertieren von Float in Byte-Array

Lesezeit: 7 Minuten

Benutzer-Avatar
Ben Wicklung

Ich versuche, eine Funktion zu erstellen, die eine Float-Variable akzeptiert und in ein Byte-Array konvertiert. Ich habe ein funktionierendes Code-Snippet gefunden, möchte es aber nach Möglichkeit in einer Funktion wiederverwenden.

Ich arbeite auch mit der Arduino-Umgebung, aber ich verstehe, dass sie die meisten C-Sprachen akzeptiert.

Derzeit funktioniert:

float_variable = 1.11;
byte bytes_array[4];

*((float *)bytes_array) = float_variable;

Was kann ich hier ändern, damit diese Funktion funktioniert?

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

Ich bin nicht so vertraut mit Zeigern und so, aber ich habe das gelesen (schweben) verwendet Casting oder so etwas?

Jede Hilfe wäre sehr willkommen!

Prost

*BEARBEITEN: GELÖST

Hier ist meine letzte Funktion, die in Arduino für jeden funktioniert, der dies findet. Es gibt effizientere Lösungen in den Antworten unten, aber ich denke, das ist in Ordnung zu verstehen.

Funktion: Konvertiert die Eingabe-Float-Variable in ein Byte-Array

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

Aufruf der Funktion

float float_example = 1.11;
byte bytes[4];

float2Bytes(float_example,&bytes[0]);

Danke für die Hilfe aller, ich habe in den letzten 20 Minuten so viel über Zeiger und Verweise gelernt, Cheers Stack Overflow!

  • Es macht keinen Sinn, sich Gedanken über die Ausführungszeit beim Kopieren von 4 Bytes zu machen, während gleichzeitig Software-Gleitkommazahlen von AVR verwendet werden. Konzentrieren Sie sich darauf, offensichtliche massive Engpässe zu beseitigen, wenn Sie Leistungsprobleme haben.

    – Ludin

    9. Juni um 7:00 Uhr

Benutzer-Avatar
Floris

Am einfachsten ist es, eine Vereinigung zu bilden:

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

Ausgabe:

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

Hinweis – es gibt keine Garantie für die Byte-Reihenfolge … sie hängt von Ihrer Maschinenarchitektur ab.

Damit Ihre Funktion funktioniert, gehen Sie wie folgt vor:

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

Oder um es wirklich zu hacken:

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

Hinweis – in beiden Fällen stelle ich sicher, dass die Daten an den als Eingabeparameter angegebenen Ort kopiert werden. Dies ist entscheidend, da lokale Variablen nach Ihrer Rückkehr nicht mehr existieren (obwohl Sie sie deklarieren könnten static, aber lass uns dir keine schlechten Gewohnheiten beibringen. Was ist, wenn die Funktion erneut aufgerufen wird…)

  • @hackks – die Vereinigung von zwei Datenelementen mit einer Größe von vier Bytes führt dazu, dass diese beiden denselben physischen Speicher belegen – also wenn ich schreibe thing.a Ich schreibe auch in das Byte-Array. Das wirst du finden &thing.a == &thing.bytes

    – Flori

    25. Juni 2014 um 23:50 Uhr

  • Ich danke dir sehr! Endlich verstehe ich das Konzept der Gewerkschaften

    – Ben Wicklung

    25. Juni 2014 um 23:58 Uhr

  • @hackks – Ich war ein bisschen überrascht über deine Frage und habe mich angesichts der Qualität deiner Eingaben hier tatsächlich selbst in Frage gestellt (“habe ich etwas wirklich Dummes gemacht? Hätte das nicht kompilieren sollen?” Ich habe mich sogar eingeschaltet -Wstrict-aliasing Suche nach Problemen…). Danke für das +.

    – Flori

    25. Juni 2014 um 23:59 Uhr

  • +1 für die Bemerkung zur Byte-Reihenfolge – dies kann explodieren, wenn Sie es auf der falschen Architektur verwenden. Es gibt wahrscheinlich eine Möglichkeit, das zu tun, was Sie wollen, ohne sich auf Endianness zu verlassen …

    – Patrick Collins

    26. Juni 2014 um 0:10 Uhr

  • Nein, der richtige Ort. Ihr erstes Argument ist ein Array aus vier Zeigern. Sollte sein: vier Zeichen. (und: Sie brauchen den Cast nicht, memcpy() nimmt zwei void-Zeiger als seine ersten beiden Argumente)

    – Wildpässer

    12. Juli 2019 um 11:07 Uhr

Hier ist eine Möglichkeit, das zu tun, was Sie wollen, die nicht kaputt geht, wenn Sie sich auf einem System mit einem anderen befinden Endianität von dem, auf dem du gerade bist:

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

Sie können es hier in Aktion sehen: http://ideone.com/umY1bB

Das Problem bei den obigen Antworten ist, dass sie sich auf die zugrunde liegende Darstellung von verlassen floats: C gibt keine Garantie dafür, dass das höchstwertige Byte “erstes” im Speicher ist. Der Standard ermöglicht die Implementierung des zugrunde liegenden Systems floatWie auch immer es sich anfühlt — wenn Sie also Ihren Code auf einem System mit einer bestimmten Art von Endianness (Byte-Reihenfolge für numerische Typen im Speicher) testen, funktioniert er nicht mehr abhängig von der Art des Prozessors, auf dem Sie es ausführen.

Das ist ein wirklich böser, schwer zu behebender Fehler, den Sie nach Möglichkeit vermeiden sollten.

  • Vielen Dank für Ihren Beitrag, es macht mir Sorgen, dass andere Systeme eine Float-Variable falsch interpretieren könnten. Wie bestimmt Ihr Beispielcode jedoch zwischen Systemen? Verzeihen Sie meine Unwissenheit, ich lerne noch…

    – Ben Wicklung

    26. Juni 2014 um 0:55 Uhr

  • @TylerDurden Es funktioniert systemübergreifend, weil es den Begriff eines Floats, mit dem das System arbeitet, nicht “bricht”. Die Verwendung einer Vereinigung interagiert direkt mit den Werten im Speicher, daher hängt es davon ab, ob das System das höchstwertige Byte im höchstwertigen Byte des Floats speichert. Die Verwendung dieser Strategie geht nur durch andere Abstraktionen — int und Bitshifting – also ist es konsistent, egal wie das zugrunde liegende System seine Bytes anordnet.

    – Patrick Collins

    26. Juni 2014 um 1:00 Uhr

  • Das wichtigste Bit von a float und ein int wird sich unabhängig von der Endianität an der gleichen Stelle befinden – Bitverschiebung und Maskierung ziehen es also korrekt heraus. Das System garantiert jedoch nicht, dass das höchstwertige Bit von a float ist das höchstwertige Bit, so dass es brechen kann, wenn Sie a verwenden union auf einem System mit einer anderen Endianness als dem, auf dem Sie entwickelt haben.

    – Patrick Collins

    26. Juni 2014 um 1:01 Uhr


  • Es hat nichts damit zu tun, dass ein Gleitkommawert falsch interpretiert wird, sondern einige Systeme stellen Zahlen dar als [MOST_SIGNIFICANT_BYTE, 2ND_MOST_SIGNIFICANT, 2ND_LEAST_SIGNIFICANT, LEAST_SIGNIFICANT] und andere repräsentieren sie als [LEAST_SIGNIFICANT, 2ND_LEAST_SIGNIFICANT, 2ND_MOST_SIGNIFICANT, MOST_SIGNIFICANT].

    – Patrick Collins

    26. Juni 2014 um 1:04 Uhr

  • & 0xFF schnappt sich das niederwertigste Byte, nicht das erste im Speicher, so dass es die Abhängigkeit von der Byte-Reihenfolge des zugrunde liegenden Systems vermeidet.

    – Patrick Collins

    26. Juni 2014 um 2:21 Uhr

Ich würde empfehlen, es mit einer “Union” zu versuchen.

Schau dir diesen Beitrag an:

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

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

In Ihrem Fall so etwas wie:

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

my_union.float_variable = 1.11;

Noch ein anderer Weg, ohne Gewerkschaften: (Annahme byte = unsigned char)

void floatToByte(byte* bytes, float f){

  int length = sizeof(float);

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

}

Benutzer-Avatar
Enzo Guerra

das scheint auch zu funktionieren

#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);

dann

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

auch für Doppel

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

uint8_t ures[siz];

memcpy (&ures, &dval, siz);

dann

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

Obwohl die anderen Antworten zeigen, wie dies mit einer Union erreicht wird, können Sie dies verwenden, um die gewünschte Funktion wie folgt zu implementieren:

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

oder

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

Benutzer-Avatar
Lundin

**conversion without memory reference** \
#define FLOAT_U32(x) ((const union {float f; uint32_t u;}) {.f = (x)}.u) // float->u32
#define U32_FLOAT(x) ((const union {float f; uint32_t u;}) {.u = (x)}.f) // u32->float

**usage example:**
float_t sensorVal = U32_FLOAT(eeprom_read_dword(&sensor));

  • Oder wenn Sie es typsicher machen möchten: {.f = _Generic((x),float:(x))}

    – Ludin

    9. Juni um 6:23

1351990cookie-checkC-Funktion zum Konvertieren von Float in Byte-Array

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy