How to combine two 32-bit integers into one 64-bit integer?
Solution 1
long long val = (long long) mostSignificantWord << 32 | leastSignificantWord;
printf( "%lli", val );
Solution 2
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
Solution 3
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;
}
Solution 4
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.
Solution 5
This code works when both upper32 and lower32 is negative:
data64 = ((LONGLONG)upper32<< 32) | ((LONGLONG)lower32& 0xffffffff);
Related videos on Youtube
bei
Updated on July 09, 2022Comments
-
bei almost 2 years
I have a count register, which is made up of two 32-bit unsigned integers, one for the higher 32 bits of the value (most significant word), and other for the lower 32 bits of the value (least significant word).
What is the best way in C to combine these two 32-bit unsigned integers and then display as a large number?
In specific:
leastSignificantWord = 4294967295; //2^32-1 printf("Counter: %u%u", mostSignificantWord,leastSignificantWord);
This would print fine.
When the number is incremented to 4294967296, I have it so the leastSignificantWord wipes to 0, and mostSignificantWord (0 initially) is now 1. The whole counter should now read 4294967296, but right now it just reads 10, because I'm just concatenating 1 from mostSignificantWord and 0 from leastSignificantWord.
How should I make it display 4294967296 instead of 10?
-
Clifford about 14 yearsYour method would only make any sense if you used %8.8X as the format specifier to concatenate the hex values of the words. For it to work in decimal, the maximum integer value would have to be (10^n)-1 where n were the number of decimal digits, and that will never be the case on a binary machine!
-
-
Alex Korban about 14 years
<< 32
will not work right ifmostSignificantWord
is a 32-bit integer. It needs to be cast to a 64-bit type first. By the way, in g++long
andlong long
are both 8 bytes. -
caf about 14 yearsIt is irrelevant whether the shift amount constant is
32
or32ULL
, since they both have the same value and that is all that's used for the shift. As long as the value being shifted has the typeunsigned long long
, everything is hunky dory. -
Adrian McCarthy about 14 yearsWhy even show the non-portable solution? Now somebody will copy it, and later the code will be moved to another application, and something will eventually break and it'll take forever to debug.
-
Steve Jessop about 14 years@Alex: in g++ on x64,
long
is 64 bits. On x86 it is 32 bits. -
Norman Ramsey about 14 yearsPlease use C99 types
uint32_t
anduint64_t
. This is why they are there. -
Norman Ramsey about 14 yearsUse the C99 types with explicit sizes. It is why they are there.
-
Norman Ramsey about 14 yearsBrilliant. Someone who not only knows how to use the C99 types with explicit sizes, but who also understands how to use the related
printf
macros. +10 if I could. SO-ers, unite! This answer deserves top billing! -
bei almost 14 yearsCould anybody break down what this line means? uint64_t i = (uint64_t) mostSignificantWord << 32 | leastSignificantWord; And what's the PRIu64? My compiler doesn't seem to like it, says "expected ) after PRIu64.
-
jfs almost 14 years@Bei337:
inttypes.h
is a part of C99 standard. It defines format specifiers forprintf/scanf
such asPRIu64
and it includesstdint.h
that defines typedefs such asuint32_t
. If you are using MSVC++/Windows then takeinttypes.h
from code.google.com/p/msinttypes -
bei almost 14 yearsGreat explanation, thank you! My compiler didn't like the macro, and I didn't want to include the header, so I used twk's implementation instead. But I learned the most from your post, thanks Sebastian.
-
jfs almost 14 years@Bei337: It might help if you compile the code as C code (not as C++). C++ requires
__STDC_FORMAT_MACROS
to be defined to usePRI..
macros. -
Premal Shah almost 12 yearshow do u reverse the process to return the 32 bit integers from the 64 bit integer?
-
jfs over 11 years@PremalShah:
uint32_t lo = (uint32_t)n, hi = (n >> 32);
, wheren
is aunit64_t
variable. -
dove over 11 yearscould you elaborate and explain your answer
-
mohit over 11 yearsthis will only work on little-endian machines.one more assumption is unsigned long is 64 bits and unsigned int is 32 bits(can use uint32 and uint64). now the explanation data64=lowerword this will copy the lowerword to the lower bytes of data64. (unsigned int)data64 convert it to 32bits. &((unsigned int)data64) gives me the address of type unsigned int adding 1 will incrrement the address to the higher bytes of data64 finally * of this stores the upperword in those higher bytes.
-
underscore_d over 5 yearsThe inputs are
unsigned
, so in the absence of the OP saying otherwise, shouldn't the output be too?long long
on its own impliessigned long long int
. -
Peter Cordes almost 5 yearsThis has undefined behaviour; it violates strict aliasing. Use
memcpy
if you want to do this, or better don't do it at all. Compilers understand((uint64_t)a<<32) | b
-
noname2019 over 3 yearsif included -Wall or -Werror -> result (error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]). Your code for gcc 8.3: uint64_t val2 = ((uint64_t)val1); only without -Wall and -Werror :(
-
caiohamamura over 3 yearsYes, strict-aliasing is expected to throw warnings, this is where you must tell the compiler you know what you are doing when you cast pointers to different types. Just be bold and decorate this piece of code with:
#pragma GCC diagnostic push
and#pragma GCC diagnostic ignored "-Wstrict-aliasing"
before the code and#pragma GCC diagnostic pop
after the code. -
caiohamamura over 3 yearsAnother way would be to derefence in two steps, instead of
uint64_t val2 = *(uint64_t*)val1
you would cast the pointer typesuint64_t *val2 = (uint64_t*)val1
and then dereference that pointeruint64_t result = *val2
, I tried it here and everything went ok. -
Andrew Henle almost 3 yearsDon't ever do this. When you write code per this answer, you're just hoping your compiler doesn't do this to you: C undefined behavior. Strict aliasing rule, or incorrect alignment?. I personally prefer code that doesn't rely on "Gee, I hope this works!!" like the code posted in this answer does. The best anyone can say about code like that posted in this answer is, "I haven't observed it to fail. Yet." Write better code than this.
-
caiohamamura almost 3 yearsWell, I don't totally agree with that. If you ever say that to anyone then it is the same as stating that anyone except experts can write any code and you would restrict anybody to actually learn anything or even use their creativity to think outside the box and create new solutions. I just had in my mind that instead of bitshifting one
uint32_t
leftwise and concatenating (OR) the other one to the right would be the same as putting two contiguosuint32_t
in memory and reading them asuint64_t
. Shouldn't I test it? I'm no expert at all but I do not like to think inside the box either.