views:

886

answers:

3

Suppose I have one long long int and want to take its bits and construct four unsigned short ints out of it.

Particular order doesn't matter much here.

I generally know that I need to shift bits and truncate to the size of unsigned short int. But I think I may make some weird mistake somewhere, so I ask.

+10  A: 
#include <stdint.h>
#include <stdio.h>

union ui64 {
    uint64_t one;
    uint16_t four[4];
};

int
main()
{
    union ui64 number = {0x123456789abcdef0};
    printf("%x %x %x %x\n", number.four[0], number.four[1],
                            number.four[2], number.four[3]);
    return 0;
}
Chris Jester-Young
Would this be influenced by little/big-endian?
slashmais
Yes, you get different results for big-endian systems vs little-endian ones.
Chris Jester-Young
It's definitely a quick-and-hacky solution, on the same order as: "@numbers = unpack 'S4', pack 'Q', $number;" in Perl
Chris Jester-Young
Will fail nastily on e.g. a Cray and a TI DSP, to name but a few CPUs where short is/can be 32 bits.
MSalters
I 100% agree that this solution is not portable at all. Perhaps I should amend the answer to use inttypes.h. :-)
Chris Jester-Young
This is really beautiful :)
Camilo Díaz
+2  A: 
union LongLongIntToThreeUnsignedShorts {
   long long int long_long_int;
   unsigned short int short_ints[sizeof(long long int) / sizeof(short int)];
};

That should do what you are thinking about, without having to mess around with bit shifting.

workmad3
Can you guarantee that the struct won't have padding between short ints?
Alexander
good point. Array method would be more sensible.
workmad3
Ooops - I originally wrote about three unsigned ints. That probably mislead you. Sorry for that. Your answer was helpful anyway.
phjr
It was <code>unsigned short int</code> originally (probably 2 bytes each -- that's OK). My point was that unless you're certain about the padding/alignment rules, that struct was dangerous.
Alexander
+3  A: 
(unsigned short)((((unsigned long long int)value)>>(x))&(0xFFFF))

where value is your long long int, and x is 0, 16, 32 or 48 for the four shorts.

moonshadow
The catch with bitshifting is that in C/C++ the behavior is undefined for negative numbers.
Alexander
For negative "value" it's not undefined, it's implementation-defined. For negative "x" it's undefined, but that never happens here. And the storage format of a negative integer is implementation-defined too (within certain possibilities), so on that score this is no worse than using a union.
Steve Jessop
I don't care if the shift sign-extends the value, I'm masking out all but the bottom sixteen bits. Those are well-defined.
moonshadow
I think Alexander is pointing out that the result of a right shift on a negative signed quantity need not be either 0-fill or sign-fill. It can be anything, as long as the compiler documents what. At least in C++. I haven't checked the C standard too. On "almost all" compilers you're right, though.
Steve Jessop
You got me: the right wording from the standard is "implementation-defined". I think the easiest fix for the solution in this answer is to convert value into an unsigned types first. Then the solution is superior to structs, because it yields the same results on big- and little-endian architectures.
Alexander
"implementation-defined" means a C++ compiler could in principle generate code that, say, does nothing, if the the left-hand side of the right-shift operator is negative.
Alexander
This would take care at least of the little/big-endian problem in the union-based answers
slashmais
I understand that in reality on most compilers the right-shift either fills the left bits with 0 or propagates the sign, but it's better to be on the safe side by converting "value" to unsigned first.
Alexander
Yes, I'd do that too - signed -> unsigned conversion is defined (in C++ at least, I still haven't checked C). So the result is the same everywhere, provided only that the types have the MIN/MAX values you expect.
Steve Jessop
Edited, then, for what it's worth. Though I've never come across an implementation that did anything other than sign-extend I guess it's better to be safe.
moonshadow