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)

yota
  • 2,020
  • 22
  • 37
  • 5
    It's not about endienness per se; the arrangement of bits seems to be [compiler-dependent](http://stackoverflow.com/questions/6043483/why-bit-endianness-is-an-issue-in-bitfields). If you want portabile code, you are probably better off using `(x.word >> bit) & 1`. – M Oehm Oct 30 '14 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? – bph Oct 30 '14 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... – V-X Oct 30 '14 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. – chux - Reinstate Monica Oct 30 '14 at 17:24
  • If you just use `uint32_t` for your type then all the "portability problems" go away. A signed integer is supposed to store values that might be negative, but if all you're doing is storing a collection of 1-bit flags then that possibility is not needed. – M.M Oct 31 '14 at 20:01
  • 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). – M.M Oct 31 '14 at 20:04

2 Answers2

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

bph
  • 10,728
  • 15
  • 60
  • 135
  • 1
    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. – yota Oct 31 '14 at 09:54
-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;
Pickiff
  • 114
  • 2
  • 2
    -1 This is compiler dependent. There is no one true way to do it in little endian systems. – user694733 Oct 30 '14 at 10:18
  • 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 :) – yota Oct 30 '14 at 10:24
  • you could argue if the little endian code you have written works then you effectively understand the bit-packing, even if it is unexpected – bph Oct 30 '14 at 10:30
  • I do understand the how, not the why :) – yota Oct 30 '14 at 14:32