views:

404

answers:

9

It's my understanding that in C/C++ bitwise operators are supposed to be endian independent and behave the way you expect. I want to make sure that I'm truly getting the most significant and least significant words out of a 64-bit value and not worry about endianness of the machine. Here's an example:

uint64_t temp;
uint32_t msw, lsw;
msw = (temp & 0xFFFFFFFF00000000) >> 32;
lsw = temp & 0x00000000FFFFFFFF;

Will this work?

+2  A: 

Yes. It should work.

Martin York
+15  A: 

6.5.7 Bitwise shift operators

4 The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1 × 2E2, reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and nonnegative value, and E1 × 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

So, yes -- guranteed by the standard.

dirkgently
A: 

I think what you are saying is quite true, but where does this get you?

If you have some literal values hanging around, then you know which end is which. But if you find yourself with values that have come from outside the program, then you can't be sure, unless they have been encoded in some way.

quamrana
A: 

In your code, you have undefined behaviour, as the variables are never initialised. In general, the truth of what you say in the question depends entirely on how the variables got their contents.

anon
Could you give some examples of ways the variable gets its value and what effects those different ways have?
Rob Kennedy
@Rob fread(), for example.
anon
Yes, go on. DoxaLogos's conclusion was that masking a 64-bit variable with 0xffffffff will yield the lower 32 bits of the value the variable holds. How does that depend on whether fread was used to store that value? Masking with that value always gives the least significant bytes of the value being operated on. If different endianness caused fread to put a different value in the variable than was originally stored in the file, then that's a problem with your use of fread, not with your use of masking or shifting to work on whatever value fread ultimately provided.
Rob Kennedy
+2  A: 

Yes, that should work. When you're retrieving the msw, your mask isn't really accomplishing much though -- the bits you mask to zero will be discarded when you do the shift anyway. Personally, I'd probably use something like this:

uint32_t lsw = -1, msw = -1;
lsw &= temp;
msw &= temp >> 32;

Of course, to produce a meaningful result, temp has to be initialized, which it wasn't in your code.

Jerry Coffin
Yeah, I wasn't concerned about the value of temp for this case, just a sanity check that I'm actually getting the msw and lsw.
DoxaLogos
Rob Kennedy
@Rob: As long as the destination is exactly the output size desired, it doesn't accomplish anything. If, however, you wanted something like the bottom 24 bits, and had to put it in a 32-bit type, then you'd need to mask it.
Jerry Coffin
+1  A: 

Just a thought I would like to share, perhaps you could get around the endianess of a value by using the functions or macros found in <arpa/inet.h>, to convert the Network to Host order and vice versa, it may be said that it is more used in conjunction to sockets, but it could be used for this instance to guarantee that a value such as 0xABCD from another processor is still 0xABCD on the Intel x86, instead of resorting to hand-coded custom functions to deal with the endian architecture....?!

Edit: Here's an article about Endianess on CodeProject and the author developed macros to deal with 64-bit values.

Hope this helps, Best regards, Tom.

tommieb75
It doesn't look to me like the poster cares about endianness, but rather 64 bits into most significant and least significant 32. Presumably the 32-bit integers will have the same endianness as the 64-bit integers.
David Thornley
A: 

In addition to the other responses, I shall add that you should not worry about endianness in C. Endianness trouble comes only from looking at some bytes under a different type than what was used to write those bytes in the first place. When you do that, you are very close to have aliasing issues, which means that your code may break when using another compiler or another optimization flag.

As long as you do not try to do such trans-type accesses, your code should be endian-neutral, and run flawlessly on both little-endian and big-endian architectures. Or, in other words, if you have endianness issues, then other kinds of bigger trouble are also lurking nearby.

Thomas Pornin
+1  A: 

It will work, but the strange propensity of some authors for doing bit-masking before bit-shifting always puzzled me.

In my opinion, a much more elegant approach would be the one that does the shift first

msw = (temp >> 32) & 0xFFFFFFFF; 
lsw = temp & 0xFFFFFFFF; 

at least because it uses the same "magic" bit-mask constant every time.

Now, if your target type is unsigned and already has the desired bit-width, masking becomes completely unnecesary

msw = temp >> 32; 
lsw = temp; 
AndreyT
Wow, I'm glad atleast one mentioned doing it this way! +1 for consistency in bit-mask :)
legends2k
+1  A: 

Endianness is about memory layout. Shifting is about bits (and bit layout). Word significance is about bit layout, not memory layout. So endianness has nothing to do with word significance.

MSN