I'm pretty sure its just a matter of some bitwise operations, I'm just not entirely sure of exactly what I should be doing, and all searches return back "64 bit vs 32 bit".
Asked
Active
Viewed 4.7k times
6 Answers
48
pack:
u32 x, y;
u64 v = ((u64)x) << 32 | y;
unpack:
x = (u32)((v & 0xFFFFFFFF00000000LL) >> 32);
y = (u32)(v & 0xFFFFFFFFLL);
Jack
- 128,551
- 28
- 227
- 331
-
36You don't need the explicit bitmasks when unpacking; `x = (u32)(v>>32); y = (u32)v;` does the job. – Mike Seymour May 11 '10 at 13:26
-
@MikeSeymour or Jack - Is it easy for you to find in the C++11 standard confirmation that this is guaranteed to work for all compliant compilers? – Dan Nissenbaum Apr 21 '14 at 00:19
-
1@DanNissenbaum: Shift operators are specified in 5.8 - well defined for all unsigned values if the shift is in range, as it is here. Unsigned integral conversions are specified in 4.7/2 - defined to preserve all the bits that fit in the new type (specified in terms of modular arithmetic). – Mike Seymour Apr 21 '14 at 10:47
12
Or this, if you're not interested in what the two 32-bits numbers mean:
u32 x[2];
u64 z;
memcpy(x,&z,sizeof(z));
memcpy(&z,x,sizeof(z));
lhf
- 67,570
- 9
- 102
- 136
-
1+1 for not introducing types and bitshifts. I'd prefer assignments over `memcpy` though. – Michael Krelin - hacker May 11 '10 at 12:03
-
2Goz, it's not about performance, it's about my (my!) perception of code beauty. – Michael Krelin - hacker May 11 '10 at 12:41
-
Be wary that some hardware is big endian and some hardware is little endian – James Wierzba Oct 27 '18 at 00:45
11
Use a union and get rid of the bit-operations:
<stdint.h> // for int32_t, int64_t
union {
int64_t big;
struct {
int32_t x;
int32_t y;
};
};
assert(&y == &x + sizeof(x));
simple as that. big consists of both x and y.
Viktor Sehr
- 12,595
- 3
- 54
- 87
-
2+1 for the union, but it would be better to use standard fixed size types from `
`, i.e. `int32_t`, `int64_t`, since sizeof(long) and sizeof(long long) are not guaranteed to be any particular value. – Paul R May 11 '10 at 12:13 -
What is an aliasing error? What should be avoided at all costs? The real problem I see here is that you can't guarantee `y` is not aligned away without taking extra measures. – Michael Krelin - hacker May 11 '10 at 12:43
-
2My mistake it leads to undefined behaviour. According to the spec, if you write to big you can't assume that x and y contain anything. The only member of the union that contains valid data is big. Most compilers DO support it but it IS undefined behaviour. You really are better off using a fixed size memcpy that any optimiser worth its salt will optimise out anyway. – Goz May 11 '10 at 13:02
-
Well, you can't assume anything according to specs, because there's no anonymous structs in there ;-) So the statement about big being the only member containing data makes about as little sense. But it's not undefined behavior - it should be either compile error or working. And I'm yet to see the compiler that goes beyond warning user against it (I do believe such a compiler exists, of course). – Michael Krelin - hacker May 11 '10 at 13:23
-
Viktor, not that it matters, but why do you add sizeof(*y*) to the address of x? ;-) – Michael Krelin - hacker May 11 '10 at 13:24
-
@Goz: because of the anonymous struct?, or cant you guaranteed anything with union? – Viktor Sehr May 11 '10 at 13:54
-
1@Viktor: You can ONLY guarantee that the element you just wrote in is valid. ie if you do myUnion.big = 64; then the only defined way to get the value again is by looking at myUnion.big. Anything else is undefined. – Goz May 11 '10 at 13:57
-
Actually, you're guaranteed to have syntax error, because there's no semicolon after struct ;-) Goz, you're confusing the hell out of everyone. Anonymous structs are indeed not standardized, but what the hell do you mean by being undefined? How can it be undefined if compiler accepts it? Can it possibly put another union member elsewhere? – Michael Krelin - hacker May 11 '10 at 19:48
-
@Goz: could you give a usage example where the behavious is undefined? is the struct (could be replaced by an array[2]) or the union the problem= – Viktor Sehr May 11 '10 at 20:46
-
Turning struct into array should please both Goz and standards and also rule out the possibility alignment troubles. – Michael Krelin - hacker May 12 '10 at 06:55
-
1@Everyone: Regardless of the anonymous-ness of any of those options if you write a value into big then the only defined way to get that value is through big. Using another entry in the union is undefined behaviour. It works on most compilers I totally agree but it is still undefined behaviour. – Goz May 12 '10 at 08:46
-
@Goz: but is it undefined to assign values to x and y, pass big around as an int64, and extract x any back via a union? I can't see which operation is undefined? – Viktor Sehr May 12 '10 at 08:59
-
Goz, now I see your point. Union guarantees that all of its members start at the same memory location. Which means, having `union { uint32_t big; uint16_t smalls[2]; uint8_t smallers[4]; };` yields perfectly defined (though architecture-dependent) behaviour. – Michael Krelin - hacker May 12 '10 at 10:30
-
-
@Everyone except Goz: http://stackoverflow.com/questions/10271929/union-for-uint32-t-and-uint8-t4-undefined-behavior – Mooing Duck Oct 17 '12 at 16:18
-
2This should be voted as the answer. Bit shifting operations assume a specific storage order of 64 bit numbers (big-endian/little-endian) and therefor inherently not portable. I assume the compiler will treat the union in the order supported by the underlying hardware/os. – theking2 Mar 03 '21 at 11:25
4
I don't know if this is any better than the union or memcpy solutions, but I had to unpack/pack signed 64bit integers and didn't really want to mask or shift anything, so I ended up simply treating the 64bit value as two 32bit values and assign them directly like so:
#include <stdio.h>
#include <stdint.h>
void repack(int64_t in)
{
int32_t a, b;
printf("input: %016llx\n", (long long int) in);
a = ((int32_t *) &in)[0];
b = ((int32_t *) &in)[1];
printf("unpacked: %08x %08x\n", b, a);
((int32_t *) &in)[0] = a;
((int32_t *) &in)[1] = b;
printf("repacked: %016llx\n\n", (long long int) in);
}
aphax
- 2,665
- 3
- 18
- 12
3
The basic method is as follows:
uint64_t int64;
uint32_t int32_1, int32_2;
int32_1 = int64 & 0xFFFFFFFF;
int32_2 = (int64 & (0xFFFFFFFF << 32) ) >> 32;
// ...
int64 = int32_1 | (int32_2 << 32);
Note that your integers must be unsigned; or the operations are undefined.
Williham Totland
- 27,735
- 5
- 49
- 68
0
long x = 0xFEDCBA9876543210;
cout << hex << "0x" << x << endl;
int a = x ;
cout << hex << "0x" << a << endl;
int b = (x >> 32);
cout << hex << "0x" << b << endl;
Sajjad Behravesh
- 11
- 3
-
1While this code snippet may be the solution, [including an explanation](//meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – HMD Dec 25 '18 at 10:35