char bzw. void in float umwandeln



  • Hallo zusammen,

    ich habe folgendes Problem:
    Ich habe einen Puffer der Form

    char mess_rxMsg[MSG_SIZE];
    

    bzw.

    LPVOID mess_rxMsg[MSG_SIZE];
    

    (geht beides)
    Daraus möchte ich die ersten vier Byte zu einem float-Wert, der ja aus 32 Bit besteht, zusammensetzen. Dieser soll dann auf der Konsole ausgegeben bzw. in eine Datei geschrieben werden. Dieser float-Wert wurde zuvor über die USB-Schnittstelle übertragen und die Funktion (wurde mir vom Hardwarehersteller in einer DLL zur Verfügung gestellt) für den Lesevorgang schreibt ihn in diesen Puffer rein.
    Jedenfalls habe ich es weder mit cast operators noch mit Zuweisungen anhand von Zeigern hinbekommen. Gibt es noch eine andere Möglichkeit?

    Viele Grüße & Danke für jede hilfreiche Antwort!



  • Bist du sicher, daß der float-Wert binär dort eingetragen wird? Wenn ja, sollte ein "reinterpret_cast<float*>(mess_rxMsg)" dir eigentlich weiterhelfen.

    (es könnte höchstens noch Probleme geben, wenn die DLL mit einem anderen Compiler oder anderen Einstellungen compiliert wurde als dein Programm - der genaue Aufbau eines float ist afaik nicht standardisiert)



  • CStoll (off) schrieb:

    Wenn ja, sollte ein "reinterpret_cast<float*>(mess_rxMsg)" dir eigentlich weiterhelfen.

    Brandgefährlich aufgrund von Alignment-Fehlern. Besser: Einen float (keinen Zeiger!) anlegen (hat dann automatisch korrektes Alignment) und in diesen die Daten mit memcpy / memmove reinkopieren. Wie der Float binär zu interpretieren ist, steht dann natürlich immernoch in den Sternen.



  • 7H3 N4C3R schrieb:

    CStoll (off) schrieb:

    Wenn ja, sollte ein "reinterpret_cast<float*>(mess_rxMsg)" dir eigentlich weiterhelfen.

    Brandgefährlich aufgrund von Alignment-Fehlern. Besser: Einen float (keinen Zeiger!) anlegen (hat dann automatisch korrektes Alignment) und in diesen die Daten mit memcpy / memmove reinkopieren. Wie der Float binär zu interpretieren ist, steht dann natürlich immernoch in den Sternen.

    Oder den Cast von CStoll nehmen und an den Kopierkonstruktor eines neuen float wertes übergeben. Da spart man sich das memcpy.

    mfg.



  • Mit memcpy/memmove geht es nicht, da dann für beide Parameter der Typ void* erwartet wird, und er sich beschwert, wenn ich als ersten Parameter den float angebe.



  • blubbfisch schrieb:

    Mit memcpy/memmove geht es nicht, da dann für beide Parameter der Typ void* erwartet wird, und er sich beschwert, wenn ich als ersten Parameter den float angebe.

    Da musst du casten.

    mfg.



  • Hab das jetzt mit

    reinterpret_cast<float*>(mess_rxMsg);
    

    gelöst. Hoffe es funktioniert so, da ich es leider im Moment noch nicht testen kann.
    Habe ich das richtig verstanden, dass mess_rxMsg ab diesem Punkt dann den Typ float* statt void * besitzt, also der Inalt der vier ersten Speicherstellen nun nicht mehr als vier char, sondern als ein einziger Wert vom Typ float interpretiert wird? Muss ich die Typumwandlung also wieder rückgängig machen, wenn ich danach wieder mit mess_rxMsg als void* arbeiten will?



  • Spinnt ihr? Klar ist die Darstellung von 'float' standardisiert: http://de.wikipedia.org/wiki/Gleitkommazahl#Gleitkommazahlen_in_der_Digitaltechnik

    Mein Vorschlag.

    float leFloat(uchar a, uchar b, uchar c, uchar d)
    {
        return a | (b << 8) | (c << 16) | (d << 24);
    }
    
    unsigned char mess_rxMsg[MSG_SIZE];
    leseNachrichtEin(mess_rxMsg, MSG_SIZE);
    
    float f = leFloat(mess_rxMsg[0], mess_rxMsg[1], mess_rxMsg[2], mess_rxMsg[3]);
    

    Wie immer ohne Gewähr. 😉

    EDIT: Fehler im Code behoben aber immernoch keine Gewähr. 😉



  • blubbfisch schrieb:

    Hab das jetzt mit

    reinterpret_cast<float*>(mess_rxMsg);
    

    gelöst. Hoffe es funktioniert so, da ich es leider im Moment noch nicht testen kann.
    Habe ich das richtig verstanden, dass mess_rxMsg ab diesem Punkt dann den Typ float* statt void * besitzt, also der Inalt der vier ersten Speicherstellen nun nicht mehr als vier char, sondern als ein einziger Wert vom Typ float interpretiert wird? Muss ich die Typumwandlung also wieder rückgängig machen, wenn ich danach wieder mit mess_rxMsg als void* arbeiten will?

    Nein, nur der Rückgabe Wert vom Cast hat den float* Typ.

    mfg.



  • joomoo schrieb:

    Oder den Cast von CStoll nehmen und an den Kopierkonstruktor eines neuen float wertes übergeben. Da spart man sich das memcpy.

    Du übersiehst das Alignment-Problem. Das Ergebnis des casts von CStoll ist ein char/void-Pointer, der in einen Float-Pointer gepresst wurde. Dereferenziert man diesen (um daraus einen neuen Float zu konstruieren), muss dieser nicht well-aligned sein und das BS reagiert vielleicht mit einem SIGBUS darauf.

    Apollon schrieb:

    Spinnt ihr?

    Nö.

    Ja, es gibt eine IEEE-Standardisierung für die Darstellung von float. Nein, sie ist nicht vorgeschrieben im C++-Standard. Selbst in IEEE-Darstellung bleibt das Endianess-Problem.



  • @Apollon: Man kann's auch übertreiben 😉

    @blubbfish: Die Daten in deinem Puffer ändern sich bei dem reinterpret_cast<> überhaupt nicht, lediglich Interpretation deiner Daten hängt davon ab, was für ein Zeiger darauf zugreift - wenn du den Rückgabewert des cast einem float* zuweist, kannst du sie hinterher sogar parallel als float oder char[] interpretieren (auch wenn das i.a. wenig Sinn - und im Extremfall viel kaputt - macht).



  • Apollon schrieb:

    float leFloat(uchar a, uchar b, uchar c, uchar d)
    {
        return a | (8 << b) | (16 << c) | (24 << d);
    }
    
    unsigned char mess_rxMsg[MSG_SIZE];
    leseNachrichtEin(mess_rxMsg, MSG_SIZE);
    
    float f = leFloat(mess_rxMsg[0], mess_rxMsg[1], mess_rxMsg[2], mess_rxMsg[3]);
    

    Danke, genau sowas hab ich gesucht!!! 😃 😉
    Hätte ich eigentlich auch selber draufkommen können... 🙄



  • 7H3 N4C3R schrieb:

    Apollon schrieb:

    Spinnt ihr?

    Nö.

    Ja, es gibt eine IEEE-Standardisierung für die Darstellung von float. Nein, sie ist nicht vorgeschrieben im C++-Standard. Selbst in IEEE-Darstellung bleibt das Endianess-Problem.

    Natuerlich legt C++ die Darstellung von Zahlen nicht fest. Diese ist durch die Hardware schon festgelegt. Auf meiner Maschine ist es die IEEE-754 Darstellung in LE Kodiert. Das Endianess-Problem ist kein Problem, wenn er in die Dokumentation des Hardwareherstellers eine Blick wirft, dann weiss er, wie die Daten zu interpretieren sind.



  • float leFloat(uchar a, uchar b, uchar c, uchar d)
    {
        return a | (b << 8) | (c << 16) | (d << 24);
    }
    

    Das liefert eine Zahl zwischen 0 und ca. 4 Mio. die in keinem (sinnvollen) Zusammenhang zur Ursprungszahl steht.

    @Blubbfisch:
    Schau in die Doku des Hardware-Herstellers, wie der Float aufgebaut ist.

    Dann kannst du den entweder über Standard-C++ in einen float übertragen (indem man sich jede Stelle anschaut und draufaddiert) oder du weißt, wie float auf deiner Plattform, auf der das Programm läuft, dargestellt wird und reinterpretierst die Daten direkt auf Hardware-Ebene.



  • 7H3 N4C3R schrieb:

    @Blubbfisch:
    Schau in die Doku des Hardware-Herstellers, wie der Float aufgebaut ist.

    Dann kannst du den entweder über Standard-C++ in einen float übertragen (indem man sich jede Stelle anschaut und draufaddiert) oder du weißt, wie float auf deiner Plattform, auf der das Programm läuft, dargestellt wird und reinterpretierst die Daten direkt auf Hardware-Ebene.

    Bis jetzt noch garnet, da ich den µC am anderen Ende der USB-Strippe auch noch programmieren muss. Hab nur die Funktionen für die Datenübertragung von Atmel übernommen. Werde das schon so hinbiegen, dass es passt. 🤡



  • probiers doch mal per stringstream also erst in eine string und dann in eine andere var...



  • Apollon schrieb:

    Natuerlich legt C++ die Darstellung von Zahlen nicht fest. Diese ist durch die Hardware schon festgelegt. Auf meiner Maschine ist es die IEEE-754 Darstellung in LE Kodiert. Das Endianess-Problem ist kein Problem, wenn er in die Dokumentation des Hardwareherstellers eine Blick wirft, dann weiss er, wie die Daten zu interpretieren sind.

    Also könnte man z.B. nicht bei einer Netzerkandwendung float Werte fehlerfrei übertragen???

    mfg.



  • joomoo schrieb:

    Also könnte man z.B. nicht bei einer Netzerkandwendung float Werte fehlerfrei übertragen???

    Im Textformat wäre sichlich nicht performant, aber (abzüglich der Fehler, die durch die Konvertierung ins Dezimalsystem entstehen) korrekt und plattformunabhängig.



  • joomoo schrieb:

    Apollon schrieb:

    Natuerlich legt C++ die Darstellung von Zahlen nicht fest. Diese ist durch die Hardware schon festgelegt. Auf meiner Maschine ist es die IEEE-754 Darstellung in LE Kodiert. Das Endianess-Problem ist kein Problem, wenn er in die Dokumentation des Hardwareherstellers eine Blick wirft, dann weiss er, wie die Daten zu interpretieren sind.

    Also könnte man z.B. nicht bei einer Netzerkandwendung float Werte fehlerfrei übertragen???

    mfg.

    Du uebertraegst keine Floats, Ints, etc. sondern Bitmuster. Wie der Empfaenger sie interpretiert ist ihm ueberlassen.



  • was mich eigentlich wundert ist.
    nach standard darf man

    char buf[sizeof(float)];
    float hans=1.125f;
    memcpy(buf,&hans,sizeof(float));
    

    und auch zurück
    das impliziert doch eigentlich das

    template <typename T>
    union fred
    {
      T val;
      char chars[sizeof(T)];
    };
    

    dass auch passen sollte


Anmelden zum Antworten