1

I made this union, in order to easily access, back and forth, between bits and integer. (in fact, I looked for a way to r/w all bits of a signed int, easily, without portability pitfall, I screwed myself nicely)

typedef union {
    struct {
         unsigned int b32   : 1;
         unsigned int b31   : 1;
         unsigned int b30   : 1;
         unsigned int b29   : 1;
         unsigned int b28   : 1;
         unsigned int b27   : 1;
         unsigned int b26   : 1;
         unsigned int b25   : 1;
         unsigned int b24   : 1;

         /* etc. */

         unsigned int b01   : 1; /* LSB */
    } field;
    uint32_t word;
    int32_t integer;
} Bit32Field_T;

As expected, each bxx field give me access to the bits of the underlying 32 byte integer.

This code however, is only valid for a sparc (big endian) CPU. For little-endian ones (x86) I have to mirror each fields, from b01 to b32, in order to get the expected behavior.

typedef union {
    struct {
         unsigned int b01   : 1; /* LSB */
         unsigned int b02   : 1;
         unsigned int b03   : 1;
         unsigned int b04   : 1;
         unsigned int b05   : 1;
         unsigned int b06   : 1;
         unsigned int b07   : 1;
         unsigned int b08   : 1;
         unsigned int b09   : 1;

         /* etc. */

         unsigned int b32   : 1;
    } field;
    uint32_t word;
    int32_t integer;
} Bit32Field_T;

I thought little-endianness was about reversing bytes not bits ! How should I understand the bit packing made ?

(everything compiled with gcc, under solaris for the BE, debian for the LE)

6
  • 5
    It's not about endienness per se; the arrangement of bits seems to be compiler-dependent. If you want portabile code, you are probably better off using (x.word >> bit) & 1. Commented Oct 30, 2014 at 10:06
  • working on a similar problem myself and for me converting little to big endian is about reversing bytes not bits, i realised this after reversing bits not bytes.. maybe a #define as to whether you are on a big/little system then an appropriate #ifdef for the two union declarations? not ideal as it requires a rebuild but maybe better than nothing? Commented Oct 30, 2014 at 10:23
  • Your number is 32 bit integer. It is composed of 4 bytes in a row. These bytes might be ordered differently on different architectures... Commented Oct 30, 2014 at 12:29
  • 1
    1) Endian issues typically are byte oriented ones, but bit endian issues do exists. 2) For max portability, consider that unsigned may be of different sizes. Maybe use fixed sized ones like uint16_t 3) For max portability, follow @M Oehm suggestion: use shifts. Commented Oct 30, 2014 at 17:24
  • 1
    the C standard requires that your compiler document how it orders its bits in a bit field, so you could try looking for that documentation (and file a bug report if it doesn't exist). Commented Oct 31, 2014 at 20:04

2 Answers 2

0

My approach for a similar problem, i.e. converting a 32 bit word from little to big-endian, was:

unsigned char buf[4];
uint32_t word;

fread(buf, sizeof(buf), 1, fp);

word = buf[0] |
    (buf[1] << 8) |
    (buf[2] << 16) |
    (buf[3] << 24);

I agree it is bizarre that your code worked, I would have expected the approach suggested by Pickif would be what is required

Prob worth taking M Oehm s advice on extracting bits if it is compiler dependent and not something to do with the hardware and ditch the union approach

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. My issue was more to understand the mirroring at bit level where I expected it at byte level o_O. Of course I switched my code to M OEhm proposal, even if its less clean sometimes.
-1

For little endian you have to do this

typedef union {
struct {
         unsigned int b24   : 1;
         unsigned int b25   : 1;
         unsigned int b26   : 1;
         unsigned int b27   : 1;
         unsigned int b28   : 1;
         unsigned int b29   : 1;
         unsigned int b30   : 1;
         unsigned int b31   : 1;
         unsigned int b32   : 1;

         unsigned int b16   : 1;
         unsigned int b17   : 1;
         unsigned int b18   : 1;
         unsigned int b19   : 1;
         unsigned int b20   : 1;
         unsigned int b21   : 1;
         unsigned int b22   : 1;
         unsigned int b23   : 1;
         unsigned int b24   : 1;

         /* etc. */

         unsigned int b08   : 1; /* LSB */
    } field;
    uint32_t word;
    int32_t integer;
} Bit32Field_T;

4 Comments

-1 This is compiler dependent. There is no one true way to do it in little endian systems.
as a matter of fact, tested and working code for little endian / gcc 4 is the one found in the second block. With consecutive b01 to b32 fields. And that's exactly what I find unexpected :)
you could argue if the little endian code you have written works then you effectively understand the bit-packing, even if it is unexpected
I do understand the how, not the why :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.