Endianness

We were having endianness issues reading in some binary data in the
Windows version of my project at work… While we were able to use the
network byte-ordering routines (htons(), etc) to swap the
integer values, byteswapping floats were a bit trickier… After beating my
head against the desk for a few hours yesterday, I gave up and went home…
When I got home I read the IEEE Floating Point spec (IEEE 754) until I
understood how they were represented in memory, and this morning I wrote
this code to properly do the swapping:

double fswap( float f ) {
union {        
float f;        
unsigned char b[4];
} u1, u2;

u1.f = f;

u2.b[0] = u1.b[3];
u2.b[1] = u1.b[2];    
u2.b[2] = u1.b[1];    
u2.b[3] = u1.b[0];

return u2.f;
}

When I first learned C a gojillion years ago I thought to myself “What
the hell could you possibly use a union for?” Guess I found
the answer… :)

You’d have to change it up a bit to swap a double… Here’s a stab at an
implementation for doubles, although I haven’t tested it:

double dswap( double d ) {    
union {
double d;
unsigned char b[8];
} u1, u2;

u1.d = d;

u2.b[0] = u1.b[7];
u2.b[1] = u1.b[6];
u2.b[2] = u1.b[5];
u2.b[3] = u1.b[4];
u2.b[4] = u1.b[3];
u2.b[5] = u1.b[2];
u2.b[6] = u1.b[1];
u2.b[7] = u1.b[0];

return u2.d;
}

Thought y’all might find it interesting, and it might be good to add to your
toolchest in case you ever run into this same problem.

4 thoughts on “Endianness

  1. I ended up having to use unions for the reiserfs endian safe code.

    There is a struct that looks like this, and must remain in little endian order due to disk format restrictions. Simply setting the values won’t work unless the entire structure is treated as CPU order, which introduces complexity that becomes a pain to manage.

    struct offset_v2 {
        __u64 k_offset : 60;
        __u64 k_type : 4;
    } __attribute__ ((__packed__));
    

    Unfortunately, this isn’t so easily handled because the split isn’t on a byte boundary.

    The resulting code wasn’t particulary pretty, but gets the job done:

    struct offset_v2 {
    #ifdef __LITTLE_ENDIAN
        /* little endian version */
        __u64 k_offset:60;
        __u64 k_type: 4;
    #else
        /* big endian version */
        __u64 k_type: 4;
        __u64 k_offset:60;
    #endif
    } __attribute__ ((__packed__));
    
    #ifndef __LITTLE_ENDIAN
    typedef union {
        struct offset_v2 offset_v2;
        __u64 linear;
    } __attribute__ ((__packed__)) offset_v2_esafe_overlay;
    
    static inline __u16 offset_v2_k_type( const struct offset_v2 *v2 )
    {
        offset_v2_esafe_overlay tmp = *(const offset_v2_esafe_overlay *)v2;
        tmp.linear = le64_to_cpu( tmp.linear );
        return (tmp.offset_v2.k_type <= TYPE_MAXTYPE)?tmp.offset_v2.k_type:TYPE_ANY;
    }
    
    static inline void set_offset_v2_k_type( struct offset_v2 *v2, int type )
    {
        offset_v2_esafe_overlay *tmp = (offset_v2_esafe_overlay *)v2;
        tmp->linear = le64_to_cpu(tmp->linear);
        tmp->offset_v2.k_type = type;
        tmp->linear = cpu_to_le64(tmp->linear);
    }
    
    static inline loff_t offset_v2_k_offset( const struct offset_v2 *v2 )
    {
        offset_v2_esafe_overlay tmp = *(const offset_v2_esafe_overlay *)v2;
        tmp.linear = le64_to_cpu( tmp.linear );
        return tmp.offset_v2.k_offset;
    }
    
    static inline void set_offset_v2_k_offset( struct offset_v2 *v2, loff_t offset ){
        offset_v2_esafe_overlay *tmp = (offset_v2_esafe_overlay *)v2;
        tmp->linear = le64_to_cpu(tmp->linear);
        tmp->offset_v2.k_offset = offset;
        tmp->linear = cpu_to_le64(tmp->linear);
    }
    #else
    # define offset_v2_k_type(v2)           ((v2)->k_type)
    # define set_offset_v2_k_type(v2,val)   (offset_v2_k_type(v2) = (val))
    # define offset_v2_k_offset(v2)         ((v2)->k_offset)
    # define set_offset_v2_k_offset(v2,val) (offset_v2_k_offset(v2) = (val))
    #endif
    
  2. What is preventing you from just using htonl() & ntohl() and casting?

    float htonf( const float f )
    {
        int i = htonl( *(int *)&f );
        return *(float *)&i;
    }
    
    float ntohf( const float f )
    {
        int i = ntohl( *(int *)&f );
        return *(float *)&i;
    }
    

    I just cut and pasted these from a google search..

    1. I thought the same thing, and tried it first, but it didn’t work (on windows). I can’t look at the windows htonl source, so I’m not sure what they are doing that would cause it to not work.

Leave a Reply