如果我有uint64_t original
和两个常规的四字节整数(有符号),我想将值存储在两个整数中,然后恢复无符号的64字节。这应该是可能的,因为在这两种情况下我们都有64位可用。我正在思考以下几点:
uint64_t test = 1350640807215539000;
int a = test >> 32; //get top 32 bits
int b = test & 0x00000000FFFFFFFF; //keep bottom 32 bits
uint64_t recover_test = ((a << 32) & b);
但这并没有让我回到测试的原始价值......我做错了什么部分?
答案 0 :(得分:8)
你可以使用一个联盟,而不是做很多容易出错的比特:
union
{
uint64_t u64;
int32_t s32[2];
} u;
u.u64 = 1350640807215539000ULL;
printf("a = %d\n", u.s32[0]);
printf("b = %d\n", u.s32[1]);
答案 1 :(得分:3)
这对我有用:
uint64_t test = 1350640807215539000;
int32_t a = test >> 32; //get top 32 bits
int32_t b = test & 0xffffffff; //keep bottom 32 bits
uint64_t recover_test = (((uint64_t)(uint32_t)a << 32) | (uint32_t)b);
请注意,你错误地在“恢复”时没有施放a
- 你需要这样做,否则移动32会使值全部“掉落”左边,因为它不够大。你也是在两个部分进行AND而不是OR-ing。
答案 2 :(得分:1)
像这样:
uint64_t recover_test = ((((uint64_t)a) << 32) | (uint32_t)b);
你需要告诉编译器首先将a提升为64位数 - 然后进行转换。否则它会在32位值内移位 - 转储高位。
@edit:哇。所以如果b签名,|带有有符号的32位值将从b的顶部提升到有符号位,在将其转换为64位后将其提升到a的最高位。因此,您需要先在|。
之前将b转换为无符号答案 3 :(得分:1)
您可能无法以完全便携的方式执行此操作。
原因是N位有符号整数只能表示2 N -1个不同的值。
如果有符号整数处于符号和幅度或1的补码表示,则尤其如此。这些表示在0附近是对称的。
即使是2的补码表示也可以在0附近对称,并且只允许2个 N -1个不同的值 - (2 N-1 -1)到2 < sup> N-1 -1(就像上面的情况一样)而不是允许2 N 不同的值从-2 N-1 到2 N-1 -1。
此外,联合技巧和N位无符号整数强制推入N位有符号整数会导致或者可能导致每个C标准的未定义行为。你想避免这种情况。
你可以这样做,但在某些平台上可能会失败:
#include <limits.h>
#if UINT_MAX >= 0xFFFFFFFF
typedef unsigned uint32;
#define UINT32_MIN UINT_MIN
#define UINT32_MAX UINT_MAX
typedef int int32;
#define INT32_MIN INT_MIN
#define INT32_MAX INT_MAX
#else
typedef unsigned long uint32;
#define UINT32_MIN ULONG_MIN
#define UINT32_MAX ULONG_MAX
typedef long int32;
#define INT32_MIN LONG_MIN
#define INT32_MAX LONG_MAX
#endif
typedef unsigned long long uint64;
#define UINT64_MAX ULLONG_MAX
#ifndef C_ASSERT
#define C_ASSERT(expr) extern char CAssertExtern[(expr)?1:-1]
#endif
// Make sure uint32 is 32 bits exactly without padding bits:
C_ASSERT(sizeof(uint32) * CHAR_BIT == 32 && UINT32_MAX == 0xFFFFFFFF);
// Make sure int32 is 32 bits exactly without padding bits and is 2's complement:
C_ASSERT(sizeof(int32) * CHAR_BIT == 32 &&
INT32_MAX == 0x7FFFFFFF && (uint32)INT32_MIN == 0x80000000);
// Make sure uint64 is 64 bits exactly without padding bits:
C_ASSERT(sizeof(uint64) * CHAR_BIT == 64 && UINT64_MAX == 0xFFFFFFFFFFFFFFFFULL);
void splitUint64IntoInt32s(uint64 x, int32* ph, int32* pl)
{
uint32 h = (uint32)(x >> 32);
uint32 l = (uint32)x;
if (h <= INT32_MAX)
*ph = h;
else
*ph = (int)(h - INT32_MAX - 1) - INT32_MAX - 1;
if (l <= INT32_MAX)
*pl = l;
else
*pl = (int)(l - INT32_MAX - 1) - INT32_MAX - 1;
}
uint64 combineInt32sIntoUint64(int32 h, int32 l)
{
return ((uint64)(uint32)h << 32) | (uint32)l;
}
gcc能够从上面生成非常优化的机器代码而无需任何算术运算:
_splitUint64IntoInt32s:
movl 8(%esp), %edx
movl 12(%esp), %eax
movl %edx, (%eax)
movl 4(%esp), %edx
movl 16(%esp), %eax
movl %edx, (%eax)
ret
_combineInt32sIntoUint64:
movl 8(%esp), %eax
movl 4(%esp), %edx
ret
答案 4 :(得分:1)
unsigned long long u64 = 0xAAAAAAAABBBBBBBB;
int l = ((int*)(&u64))[0];
int h = ((int*)(&u64))[1];
unsigned long long restored;
((int*)(&restored))[0] = l;
((int*)(&restored))[1] = h;
答案 5 :(得分:0)
我做错了什么部分?
uint64_t recover_test = ((a << 32) & b);
a << 32
是未定义的行为。
左移位的左移位或与左操作数的位宽相同是不确定的。
答案 6 :(得分:0)
这有效
#include <stdint.h>
#include <stdio.h>
int main(void)
{
uint64_t test = 1350640807215539000;
int a = test >> 32; //get top 32 bits
int b = test & 0x00000000FFFFFFFF; //keep bottom 32 bits
uint64_t recover_test = (((uint64_t)((uint32_t)a) << 32) | (uint32_t)b);
printf("a: %llu\n", (uint64_t)a);
printf("b: %llu\n", (uint64_t)b);
printf("Recovered: %llu\n", recover_test);
printf("Test %s\n", test == recover_test ? "PASSED" : "FAILED");
}
与其他所有内容完全相同,我只是在将int用于任何地方之前将其明确地转换回无符号整数。如果a
或b
最终为负数,这可能很重要。
输出:
a: 314470568
b: 2100994872
Recovered: 1350640807215539000
Test PASSED
b为负时的输出(谢谢Daniel):
a: 314470568
b: 18446744073663062840 # -46488776
Recovered: 1350640809363022648
Test PASSED