Endianness



  • Das hat doch damit nichts zu tun. Wenn du weißt das die Datei in Network Byte Order erstellt wurde. Dann musst du die Daten dem entsprechend auch nach dem auslesen noch konvertieren bevor du sie verwenden kannst.

    Das reinterpret_cast brauchst du vielleicht um aus dem int i = 0; ein char * p = reinterpret_cast< char *> (&i); zu machen bevor du es zum auslesen übergibst an ifstream::read() z.B. aber du musst dennoch im nachhinein die daten konvertieren wenn das system auf dem deine Anwendung läuft nicht die richtig endianess hat. Sonst hast du hinterher nur Daten die nicht richtig ausgwertet werden können.



  • Bis eben dachte ich eigentlich, ich hätte es einigermaßen verstanden...

    Ich habe doch etwas in dieser Art:

    float readFloat(ifstream& file)
    {
        char bytes[4];
        for (int i = 0; i < 4; i++)
            bytes[i] = file.get();
    
        return *reinterpret_cast<float*>(bytes);
    }
    

    Damit der reinterpret_cast ein sinnvolles Ergebnis liefert, muss ich doch entweder die Bytes (beim Einlesen) entsprechend umsortieren, wenn die Endianess im File anders ist als auf dem System, oder eben aus einem File lesen, in dem Endianness der des Sytems entspricht, oder? 😕



  • Nanuq schrieb:

    Bis eben dachte ich eigentlich, ich hätte es einigermaßen verstanden...

    Ich habe doch etwas in dieser Art:

    float readFloat(ifstream& file)
    {
        char bytes[4];
        for (int i = 0; i < 4; i++)
            bytes[i] = file.get();
    
        return *reinterpret_cast<float*>(bytes);
    }
    

    Damit der reinterpret_cast ein sinnvolles Ergebnis liefert, muss ich doch entweder die Bytes (beim Einlesen) entsprechend umsortieren, wenn die Endianess im File anders ist als auf dem System, oder eben aus einem File lesen, in dem Endianness der des Sytems entspricht, oder? 😕

    Ja, einfach ein

    std::reverse(bytes, bytes+sizeof(bytes));
    

    machen, wenn die Byteorder der Datei und des Systems nicht übereinstimmt.

    Problematisch wird es natürlich nur, wenn die Darstellung eines floats nicht die gleiche ist ;). Aber dank IEEE754 sollte das nur auf obskuren Systemen passieren.



  • Es würde wohl eher so in der art aussehen:

    float readFloat(ifstream& file)
    {
       boost::  result = 0;
       file.read( reinterpret_cast< char * >( &result ), sizeof(int32_t) );   
       result = ntohl(result); // Von BigEndian zu HostByteOrder (Mehr oder weniger NOP wenn es auf einer BigEndian platform läuft )
       return *reinterpret_cast<float*>(&result);
    }
    

    Wobei mir das mit dem float nicht wirklich gefällt. Da davon ausgegangen wird das es 32 Bit hat, was nicht umbedingt richtig ist.


  • Mod

    rüdiger schrieb:

    Problematisch wird es natürlich nur, wenn die Darstellung eines floats nicht die gleiche ist ;). Aber dank IEEE754 sollte das nur auf obskuren Systemen passieren.

    Undefiniert ist es in jedem Fall, weil ein float gelesen wird, wo keins ist. Bedenkenswert bei Compilern wie gcc, die recht agressiv mit Aliasing umgehen.

    float readFloat(ifstream& file)
    {
        float value;
        for (int i = 0; i < sizeof value; i++)
            reinterpret_cast<char(&)[sizeof value]>(value)[i] = file.get();
    
        return value;
    }
    

    ist hingegen unproblematisch (gleiche Kodierung vorausgesetzt).



  • camper schrieb:

    Undefiniert ist es in jedem Fall, weil ein float gelesen wird, wo keins ist. Bedenkenswert bei Compilern wie gcc, die recht agressiv mit Aliasing umgehen.

    Was hilft uns das Bedenken von "aggresive aliasing" wenn wir so einlesen müssen weil es die API nicht anders zulassen würde?

    Das ist etwas was mir nicht so richtig in den Kopf will. Wenns umbedingt sein muss nimmt man dann halt ein union. (Was soweit ich das verstanden habe auch nicht wirklich viel hilft beim gcc)

    float readFloat(ifstream& file)
    {
        float value;
        for (int i = 0; i < sizeof value; i++)
            reinterpret_cast<char(&)[sizeof value]>(value)[i] = file.get();
    
        return value;
    }
    

    ist hingegen unproblematisch (gleiche Kodierung vorausgesetzt).

    Trotzdem musst du hier noch die endianess ändern oder nicht?
    Und lesen byte für byte kann auch nicht das wahre sein...



  • Da habe ich ja eine interessante Diskussion angestoßen...

    camper schrieb:

    rüdiger schrieb:

    Problematisch wird es natürlich nur, wenn die Darstellung eines floats nicht die gleiche ist ;). Aber dank IEEE754 sollte das nur auf obskuren Systemen passieren.

    Undefiniert ist es in jedem Fall, weil ein float gelesen wird, wo keins ist. Bedenkenswert bei Compilern wie gcc, die recht agressiv mit Aliasing umgehen.

    float readFloat(ifstream& file)
    {
        float value;
        for (int i = 0; i < sizeof value; i++)
            reinterpret_cast<char(&)[sizeof value]>(value)[i] = file.get();
    
        return value;
    }
    

    ist hingegen unproblematisch (gleiche Kodierung vorausgesetzt).

    Wenn ich das richtig verstehe, wird hier durch den reinterpret_cast ein float als eine Referenz auf ein char-Array selber Größe interpretiert und dessen einzelne Elemente byte-weise (bzw. char-weise) befüllt. Hätte ich ein Big Endian System aber ein Little Endian File, müsste ich eben in umgekehrter Reihenfolge befüllen, richtig?

    Letzten Endes funktioniert das so für mich, mein Progrämmchen wird ohnehin nicht zu sämtlichen Systemen kompatibel sein und mich wird auch niemand zur Verantwortung ziehen, wenn mal etwas schief geht ;).
    Eine spitzfindige Kleinigkeit würde mich aber trotzdem noch interessieren. Ich weiß, dass ein Wert in der zu lesenden Datei 4 Byte entspricht (laut Erzeuger des Files eigentlich "REAL*4"). Sollte ich dann nicht in jedem Fall 4 byte einlesen? Das sizeof(value) im obigen Fall dient doch vermutlich dazu auch Fälle zu berücksichtigen in denen ein float einmal nicht 32 Bit hat (oder gibt es das gar nicht?). Wie ich dann die 4 Byte in dieses "Nicht 4 Byte float" konvertiere möchte ich dann eigentlich gar nicht mehr wissen, glaube ich.

    Danke jedenfalls für eure Erläuterungen!



  • camper schrieb:

    rüdiger schrieb:

    Problematisch wird es natürlich nur, wenn die Darstellung eines floats nicht die gleiche ist ;). Aber dank IEEE754 sollte das nur auf obskuren Systemen passieren.

    Undefiniert ist es in jedem Fall, weil ein float gelesen wird, wo keins ist. Bedenkenswert bei Compilern wie gcc, die recht agressiv mit Aliasing umgehen.

    float readFloat(ifstream& file)
    {
        float value;
        for (int i = 0; i < sizeof value; i++)
            reinterpret_cast<char(&)[sizeof value]>(value)[i] = file.get();
    
        return value;
    }
    

    ist hingegen unproblematisch (gleiche Kodierung vorausgesetzt).

    Ist char* nicht die Ausnahme bei den Aliasing-Regeln? (Der GCC warnt auch zumindest nicht)



  • Wie wäre damit, die Daten ins ASCII-Format zu serialisieren? Dann gibt es solche Probleme nicht.


  • Mod

    rüdiger schrieb:

    Ist char* nicht die Ausnahme bei den Aliasing-Regeln?

    Richtig, was der Grund dafür ist, dass wenn an der Stelle zuvor ein float definiert wurde, sich dort auch hinterher ein float befindet (bzw. befinden könnte) und dank des char-Kopierens ist der Inhalt auch nicht mehr unbestimmt.
    Wurde dagegen ein char-Array deklariert, entsteht an dieser Stelle nicht plötzlich dadurch ein float, dass man das Array zeichenweise befüllt.


Anmelden zum Antworten