Wie man float in Byte?
Ich bin mit dem HIDAPI, um Daten zu senden, um ein USB-Gerät. Diese Daten können nur gesendet werden, als byte array und muss ich senden Sie ein paar float
zahlen in das Daten-array. Ich weiß, schwimmt haben 4
bytes. Also dachte ich, das könnte funktionieren:
float f = 0.6;
char data[4];
data[0] = (int) f >> 24;
data[1] = (int) f >> 16;
data[2] = (int) f >> 8;
data[3] = (int) f;
Und später alles was ich zu tun hatte, war:
g = (float)((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3]) );
Aber testen das zeigt mir, dass die Zeilen wie data[0] = (int) f >> 24;
gibt immer 0
. Was ist Los mit meinem code und wie mache ich das richtig (D. H. das brechen eines float
innere Daten in 4 char
bytes und die Neuerstellung der gleichen float
später)?
BEARBEITEN:
War ich in der Lage, dies zu erreichen mit den folgenden codes:
float f = 0.1;
unsigned char *pc;
pc = (unsigned char*)&f;
//0.6 in float
pc[0] = 0x9A;
pc[1] = 0x99;
pc[2] = 0x19;
pc[3] = 0x3F;
std::cout << f << std::endl; //will print 0.6
und
*(unsigned int*)&f = (0x3F << 24) | (0x19 << 16) | (0x99 << 8) | (0x9A << 0);
Ich weiß memcpy()
ist ein "sauberer" Weg, es zu tun, aber auf diese Weise ich denke, die performance ist etwas besser.
- Der Grund
(int)f >> 24
zurück0
ist, dass dieint
gegossenf
gleich0
in den ersten Platz: der cast sendet die Schwimmer auf Ihrem Boden. Es ist Undefiniertes Verhalten, aber es zu tun, dass hacky so, als würden Sie etwas brauchen, wie*(int*)&f >> 24
.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Kannst du es so machen:
Damit dies funktioniert, beide Rechner müssen die gleiche floating-point-Darstellungen.
So wurde zu Recht darauf hingewiesen, in den Kommentaren, die Sie nicht unbedingt tun müssen, um die zusätzlichen
memcpy
; statt, können Sie Sie behandelnf
direkt als ein array von Zeichen (jeder Fehler). Sie haben noch zu tunmemcpy
auf der empfangenden Seite, obwohl, da können Sie nicht behandeln eine beliebige Reihe von Zeichen, die als float! Beispiel:float
,0.6
ist eine Konstante vom Typdouble
. Eine Konstante vom Typdouble
umgewandelt werden würde, umfloat
automatisch sowieso (aber naiv-Compiler generieren, schlechter code fürf = 0.6
, und einige Plattformen könnten Runde anders). Es gibt andere Möglichkeiten byte-level-Zugriff, abermemcpy
ist der beste Weg hier.f
direkt als array von chars:char const * data = (char const *)&f;
jetzt verwendendata[i]
für eine Reihe voni
.f
oderd
suffix. Having said, die, ich muss zugeben, ich Neige dazu, dies nur tun, wenn ich entweder müssen sicherstellen, dass es die richtige geben, oder müssen sicherstellen, dass der Leser versteht, welche Art es ist.float
literal. Ich dachte, das ist das, was erforderlich ist, also das ist, was ich gebe.In standard-C ist garantiert, dass jede Art zugegriffen werden kann als ein array von bytes.
Ein gerader Weg, dies zu tun ist, natürlich, durch den Einsatz der Gewerkschaften:
Wie Sie sehen können, ist es nicht notwendig, überhaupt zu verwenden memcpy oder Zeiger...
Den
union
Ansatz ist leicht zu verstehen, standard und schnell.BEARBEITEN.
Ich werde erklären, warum dieser Ansatz ist gültig in C (C99).
CHAR_BIT
bits (eine integer-Konstante >= 8, in fast allen Fällen ist 8).unsigned char
Art nutzt alle seine bits zur Darstellung des Wertes des Objekts, die eine nicht-negative Zahl, in eine rein binäre Darstellung. Dies bedeutet, dass es keine Polsterung bits oder bits verwendet, für alle anderen extrange purpouse. (Das gleiche ist nicht eine Garantie fürsigned char
oderchar
Arten).unsigned char [n]
(z.B. durch memcpy); [...]"Mehr Informationen:
Eine Diskussion in google groups
Typ-Zweideutigkeiten
EDIT 2
Weiteres detail der standard C99:
int
innerhalb derfor
Schleife ist nur gültig in C99). Allerdings ist die Frage, hat der tagc
aber nichtc++
.float
Objekt mitunsigned char[]
. Dies ist sehr spezifisch, und ich denke, es ist standard in C++ auch. Ein Undefiniertes Verhalten aufweist, indem eine andere Art, dieunsigned char
. siehe diese Diskussionunion
+unsigned char []
ist nützlich, um das "Lesen" der bytes, die ein Objekt, aber wohl die Absicht, "write-back", die bytes in das array, um die "Wiederherstellung" des ursprünglichen Objekts ist nicht legal (es wäre Undefiniertes Verhalten). Ich bin kein Experte in C++. Aber ich denke, dass der "Geist" von C++ zu halten, sich als ein sehr high-level-Sprache, so sollten Sie es vermeiden, "low-level hacks". Wahrscheinlich müssen Sie tun mehr Forschung über diefloat
Darstellung in C.unsigned char
, denn es ist die einzige Art, die in C mit der exakten binären Darstellung garantiert durch die Norm. Nun, wenn Sie schreiben, ein Mitglied einerunion
, können Sie sicher sein, was passiert, wenn Sie Lesen, ein anderes Mitglied es? (Fast) nur arrays aus char. Typen sind garantiert zu arbeiten.Die Sprache C garantiert, dass jeder Wert von jedem type1 zugegriffen werden kann als ein array von bytes. Die Art der bytes ist
unsigned char
. Hier ist ein low-level-Weg zu kopieren ein float in ein array von bytes.sizeof(f)
ist die Anzahl der bytes zum speichern des Wertes der variablef
; Sie können auchsizeof(float)
(Sie können entweder übergebensizeof
eine variable oder ein komplexer Ausdruck, oder seine Art).Funktionen
memcpy
odermemmove
genau das tun (oder eine optimierte version davon).Brauchen Sie nicht einmal zu machen, diese zu kopieren, obwohl. Sie können direkt übergeben wird ein pointer auf die float-zu den write-to-USB-Funktion und sagt ihm, wie viele bytes zu kopieren (
sizeof(f)
). Sie müssen einen expliziten cast, wenn die Funktion nimmt einen Zeiger-argument andere alsvoid*
.Hinweis: dies funktioniert nur, wenn das Gerät verwendet die gleiche Darstellung von floating-point-zahlen, die Häufig, aber nicht universell. Die meisten Maschinen verwenden Sie die IEEE-floating-point-Formate, aber Sie müssen möglicherweise wechseln der endianness.
Als für das, was falsch ist mit Ihrem Versuch: den
>>
Betreiber betreibt auf Ganzzahlen. In dem Ausdruck(int) f >> 24
,f
ist gegossen, um eineint
; wenn Sie geschrieben hattenf >> 24
ohne den cast,f
würde trotzdem automatisch in eineint
. Die Konvertierung einer Gleitkommazahl in eine Ganzzahl kommt es durch abschneiden oder Runden (in der Regel gegen 0, aber die Regel ist von der Plattform abhängig). 0.6 gerundet auf eine ganze Zahl 0 oder 1, sodata[0]
ist 0 oder 1 und die anderen 0.Müssen Sie handeln auf die bytes des float-Objekts, nicht auf den Wert.
1 Ohne Funktionen, die nicht wirklich bearbeitet werden, die in C, aber einschließlich der Funktions-Zeiger, die Funktionen decay automatisch.
for
? mit<<
und|
Aufteilung der bytes?unsigned char *
hat im wesentlichen die gleiche Sache.Vorausgesetzt, dass beide Geräte haben die gleiche Vorstellung davon, wie Schwimmern vertreten sind, warum dann nicht einfach eine
memcpy
. ich.eist der sicherste Weg, dies zu tun, wenn Sie die Kontrolle über beide Seiten zu senden, eine Art standardisierte Darstellung... dies ist nicht die effizienteste, aber es ist nicht zu schlecht für kleine zahlen.
konvertiert in float mit library-Funktion
atof
oderatof_l
.natürlich ist das nicht die optimalste, aber es wird sicher einfach sein, zu Debuggen.
wollte man mehr optimiert und kreativ, erstes byte ist Länge anschließend der exponent, dann jedes byte mit 2 Nachkommastellen... also
34.56
wirdchar array[] = {4,-2,34,56};
sowas wäre tragbar... ich würde nur versuchen, nicht zu übergeben Binär float-Darstellungen herum... da kann es schnell chaotisch.Könnte es sicherer sein, um der union die float und char-array. Setzen Sie in der float-Mitglied ist, ziehen Sie die 4 (oder was auch immer die Länge) bytes.
signed long
mit padding-bits, zum Beispiel? Nurunsigned char
ist garantiert kompatibel zu sein mit der Absicht, "abrufen" werden die bits eines Objekts. Aber "kompatibel" bedeutet nicht "zugänglich". Um sicherzustellen, dass die bytes "erreichbar" mit einem anderen Mitglied, wir brauchen eine weitere Forderung der Norm. Ich denke, man kann "gelesen" werden bytes, aber vielleicht "writing back" - bytes ist UB. Überprüfen Sie meine Antwort für weitere details.