How to combine two 32-bit integers into one 64-bit integer?
There's another way using arrays and pointers:
#include <stdio.h>
#include <inttypes.h>
int main(void) {
// Two uint32_t to one uint64_t
uint32_t val1[2] = {1000, 90000};
uint64_t *val1_u64_ptr = (uint64_t*)val1; //intermediate pointer cast to avoid Wstrict-aliasing warnings
uint64_t val2 = *val1_u64_ptr;
printf("val2: %" PRIu64 "\n", val2);
// val2: 386547056641000
// back to uint32_t array from uint64_t
uint64_t val3 = 386547056641000ull;
uint32_t *val4 = (uint32_t*)&val3;
printf("val4: %" PRIu32 ", %" PRIu32 "\n", val4[0], val4[1]);
// val4: 1000, 90000
return 0;
}
This code for me is much easier to understand and read. You are just creating a contiguous space in memory with two 32-bit unsigned int
and then this same memory space is read as a single 64-bit unsigned int
value and vice-versa. There are no operations involved only memory being read as different types.
EDIT
Forgot to mention that this is great if you already have a 64-bit array
read from somewhere then you could easily read everything as 32-bit array
pairs:
#include <stdio.h>
#include <inttypes.h>
int main() {
uint64_t array64[] = {
386547056641000ull,
93929935171414ull,
186655006591110ull,
73141496240875ull,
161460097995400ull,
351282298325439ull,
97310615654411ull,
104561732955680ull,
383587691986172ull,
386547056641000ull
};
int n_items = sizeof(array64) / sizeof(array64[0]);
uint32_t* array32 = (uint32_t*)&array64;
for (int ii = 0; ii < n_items * 2; ii += 2) {
printf("[%" PRIu32 ", %" PRIu32 "]\n", array32[ii], array32[ii + 1]);
}
return 0;
}
Output:
[1000, 90000]
[3295375190, 21869]
[22874246, 43459]
[2498157291, 17029]
[3687404168, 37592]
[1218152895, 81789]
[3836596235, 22656]
[754134560, 24345]
[4162780412, 89310]
[1000, 90000]
Using union struct
Still better and more readable would be to use a struct union as from https://stackoverflow.com/a/2810339/2548351:
#include <stdio.h>
#include <inttypes.h>
typedef union {
int64_t big;
struct {
int32_t x;
int32_t y;
};
} xy_t;
int main() {
// initialize from 64-bit
xy_t value = {386547056641000ull};
printf("[%" PRIu32 ",%" PRIu32 "]\n", value.x, value.y);
// [1000, 90000]
// initialize as two 32-bit
xy_t value2 = {.x = 1000, .y = 90000};
printf("%" PRIu64, value.big);
// 386547056641000
return 0;
}
long long val = (long long) mostSignificantWord << 32 | leastSignificantWord;
printf( "%lli", val );
my take:
unsigned int low = <SOME-32-BIT-CONSTRANT>
unsigned int high = <SOME-32-BIT-CONSTANT>
unsigned long long data64;
data64 = (unsigned long long) high << 32 | low;
printf ("%llx\n", data64); /* hexadecimal output */
printf ("%lld\n", data64); /* decimal output */
Another approach:
unsigned int low = <SOME-32-BIT-CONSTRANT>
unsigned int high = <SOME-32-BIT-CONSTANT>
unsigned long long data64;
unsigned char * ptr = (unsigned char *) &data;
memcpy (ptr+0, &low, 4);
memcpy (ptr+4, &high, 4);
printf ("%llx\n", data64); /* hexadecimal output */
printf ("%lld\n", data64); /* decimal output */
Both versions work, and they will have similar performance (the compiler will optimize the memcpy away).
The second version does not work with big-endian targets but otoh it takes the guess-work away if the constant 32 should be 32 or 32ull. Something I'm never sure when I see shifts with constants greater than 31.
It might be advantageous to use unsigned integers with explicit sizes in this case:
#include <stdio.h>
#include <inttypes.h>
int main(void) {
uint32_t leastSignificantWord = 0;
uint32_t mostSignificantWord = 1;
uint64_t i = (uint64_t) mostSignificantWord << 32 | leastSignificantWord;
printf("%" PRIu64 "\n", i);
return 0;
}
Output
4294967296
Break down of (uint64_t) mostSignificantWord << 32 | leastSignificantWord
(typename)
does typecasting in C. It changes value data type totypename
.(uint64_t) 0x00000001 -> 0x0000000000000001
<<
does left shift. In C left shift on unsigned integers performs logical shift.0x0000000000000001 << 32 -> 0x0000000100000000
|
does 'bitwise or' (logical OR on bits of the operands).0b0101 | 0b1001 -> 0b1101