Thread: Changing endianness

  1. #1
    Caution: Wet Floor
    Join Date
    May 2006
    Posts
    55

    Changing endianness

    I'm trying to swap endianness in a big-endian file: my platform is little-endian, and so things are coming out wierd:

    Code:
    #include <iostream>
    #include <fstream>
    
    using namespace std;
    
    // Endianness issue
    
    inline void endian_swap(int& x) {
    
            // Int is 4 bytes on this machine
    
            x = (x >> 24) |
            ((x<<8) & 0x00FF0000) |
            ((x>>8) & 0x0000FF00) |
            (x<<24);
    }
    
    int main(int argc, char** argv) {
    
    
            const char * gcaf=argv[1];
    
            const float GCA_VERSION=5.0;
            const int GCA_NO_MRF = 1;
            const int GIBBS_NEIGHBORHOOD=6;
            const int GIBBS_NEIGHBORS=GIBBS_NEIGHBORHOOD;
            const int MAX_LABELS=20;
    
            int * v = new int;
    
            ifstream GCA;
    
            GCA.open(gcaf, ifstream::binary);
    
    
            GCA.read((char*)v, sizeof(float));
    
            endian_swap(v[0]);
            if (v[0] == GCA_VERSION) puts("hooray!");
            else puts("darn");
    
    
    // gives the wrong answer!!!
            cout << v[0] << '\n';
    }
    The first four bytes in the file correspond to a `float', but I couldn't use the binary operators `>>' or `<<' with a float on either side, which is why the signature has an "int&". Any suggestions for reading in the file as little-endian? I tried googling, but didn't get much.

  2. #2
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    as I understand you need an reinterpreter_cast here
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  3. #3
    Registered User
    Join Date
    Oct 2008
    Posts
    1,262
    The int should be an unsigned int as well.

  4. #4
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    You could probably turn it into a template pretty easily so it will swap the endiness of any size of int.
    "I am probably the laziest programmer on the planet, a fact with which anyone who has ever seen my code will agree." - esbo, 11/15/2008

    "the internet is a scary place to be thats why i dont use it much." - billet, 03/17/2010

  5. #5
    Caution: Wet Floor
    Join Date
    May 2006
    Posts
    55
    Thanks. I changed the following lines
    Code:
    inline void endian_swap(unsigned int& x) {
    // ...
    unsigned int * v = new unsigned int[1];
    //...
    GCA.read(reinterpret_cast<char*>(v), sizeof(unsigned int));
    //...
    cout << static_cast<float>(v[0]) << '\n'; // The first four bytes of the file correspond to 
                                                                    // a float
    but still got a spurious value for v[0]:

    Code:
    % ./program <file>
    1.08423e+09
    The output should be 5.0 (this is what I get on a big-endian system, anyway). What's going wrong?

  6. #6
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    >> static_cast<float>(v[0])

    You're casting an int to float. You need to cast the int* to a float* and then dereference.

    >> inline void endian_swap(int& x) {

    Be careful, though, as sizeof( int ) may not be 4.

    >> You could probably turn it into a template pretty easily so it will swap the endiness of any size of int.

    This should work with any POD:

    Code:
    inline bool little_endian( void )
    {
        typedef unsigned char* pbyte;
        static unsigned long word = 0x1;
        static bool is_little_endian_machine = ( *pbyte( &word ) == 0x1 );
        return is_little_endian_machine;    
    }
    
    inline bool big_endian( void )
    {
        return !little_endian( );
    }
    
    namespace detail {
    
    template < typename Type >
    Type& endian_conversion_dispatcher_( Type* value, bool little_endian_mode )
    {
        typedef unsigned char* pbyte;
        if( little_endian( ) != little_endian_mode )
            std::reverse( pbyte( value ), pbyte( value ) + sizeof( Type ) );
        return *value;    
    }
    
    template < typename Type >
    inline Type endian_conversion_dispatcher_( Type const& value, bool little_endian_mode )
    {
        Type result = value;
        return endian_conversion_dispatcher_( &result, little_endian_mode );    
    }
    
    } // namespace detail
    
    template < typename Type >
    inline Type& little_endian( Type* value )
    {
        return detail::endian_conversion_dispatcher_( value, true );    
    }
    
    template < typename Type >
    inline Type little_endian( Type const& value )
    {
        return detail::endian_conversion_dispatcher_( value, true );    
    }
    
    template < typename Type >
    inline Type& big_endian( Type* value )
    {
        return detail::endian_conversion_dispatcher_( value, false );    
    }
    
    template < typename Type >
    inline Type big_endian( Type const& value )
    {
        return detail::endian_conversion_dispatcher_( value, false );    
    }
    Another benefit here is that the template automatically detects the endianess of the machine it's running on, so that you don't have to change the user side of the code. So for instance, let's say you have a float value you want to store in a file in big-endian format. Right before the value is written to the file, it is passed through the big_endian function. Likewise, when the value is later read from a file, the big_endian function is used to convert it back. (Note that the functions that take no arguments are only for implementing certain optimizations, and needn't be used as logic during conversion.)
    Last edited by Sebastiani; 03-25-2009 at 08:32 PM. Reason: elaboration
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  7. #7
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    I thought floating point numbers can't easily be sent over the network the way integers can?
    Endianness - Wikipedia, the free encyclopedia
    Is there any point in changing the endianess of floating point numbers?
    "I am probably the laziest programmer on the planet, a fact with which anyone who has ever seen my code will agree." - esbo, 11/15/2008

    "the internet is a scary place to be thats why i dont use it much." - billet, 03/17/2010

  8. #8
    int x = *((int *) NULL); Cactus_Hugger's Avatar
    Join Date
    Jul 2003
    Location
    Banks of the River Styx
    Posts
    902
    I thought floating point numbers can't easily be sent over the network the way integers can?
    You can, as long as you're not ignorant of what you're sending... if it were me, IEEE-754, big endian. No different from other types of serialization. (Granted, it might not have a standard attached to it...)
    If you know that a certain floating point number has a certain range, say [0, 1), I've seen people send an integer, like, where 0 = 0.0, and 0xFFFF = 65535/65536.0. (Using more or less bytes depending on how accurate you need.)
    long time; /* know C? */
    Unprecedented performance: Nothing ever ran this slow before.
    Any sufficiently advanced bug is indistinguishable from a feature.
    Real Programmers confuse Halloween and Christmas, because dec 25 == oct 31.
    The best way to accelerate an IBM is at 9.8 m/s/s.
    recursion (re - cur' - zhun) n. 1. (see recursion)

  9. #9
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Maybe not. I guess they would have to be serialized in some other way.
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  10. #10
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    I think I heard someone here say that if you don't know what floating point format each end is using, floating point numbers should be converted to strings before being sent over the network, then changed back to floats on the other end.
    "I am probably the laziest programmer on the planet, a fact with which anyone who has ever seen my code will agree." - esbo, 11/15/2008

    "the internet is a scary place to be thats why i dont use it much." - billet, 03/17/2010

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Advice changing a function please
    By cjohnman in forum C Programming
    Replies: 16
    Last Post: 05-12-2008, 12:56 PM
  2. Changing windows without changing?
    By Lionmane in forum Windows Programming
    Replies: 7
    Last Post: 10-19-2005, 11:41 AM
  3. [C++/WinAPI] Changing bitmap contrast
    By jagi in forum Windows Programming
    Replies: 0
    Last Post: 03-27-2005, 03:51 PM
  4. stop IP from changing
    By b00l34n in forum Networking/Device Communication
    Replies: 5
    Last Post: 07-16-2004, 09:28 AM
  5. Changing default unit in BCB 5
    By Mario in forum C++ Programming
    Replies: 0
    Last Post: 05-26-2002, 09:22 AM