C-Funktion zum Konvertieren von Float in Byte-Array
Lesezeit: 7 Minuten
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.
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);
}
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
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:
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;
}
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.
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:
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