2

如果我有一个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);

但这并没有让我回到测试的原始价值……我做错了什么?

4

7 回答 7

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]);
于 2012-10-23T21:11:03.747 回答
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-ing 而不是 OR-ing 这两个部分。

于 2012-10-23T21:00:38.947 回答
1

像这样:

uint64_t recover_test = ((((uint64_t)a) << 32) | (uint32_t)b);

您需要告诉编译器首先将 a 提升为 64 位数字 - 然后进行移位。否则,它会在 32 位值内移动 - 转储高位。

@编辑:哇。所以如果 b 是有符号的,| 具有带符号的 32 位值将在转换为 64 位后将带符号位从 b 的顶部提升到 a 的顶部。所以你需要在 | 之前先将 b 强制转换为无符号。

于 2012-10-23T20:58:36.843 回答
1

您可能无法以完全可移植的方式执行此操作。

原因是 N 位有符号整数可能仅足以表示 2 N -1 个不同的值。

如果有符号整数在符号和大小或 1 的补码表示中,情况尤其如此。这些表示在 0 附近是对称的。

甚至 2 的补码表示也可能在 0 附近对称,并且仅允许从 -(2 N-1 -1) 到 2 N-1 -1 的 2 N -1 个不同值(就像在上述情况下一样),而不是允许 2 N从 -2 N-1到 2 N-1 -1 的不同值。

此外,根据 C 标准,联合技巧和将 N 位无符号整数强制推入 N 位有符号整数确实或可能导致未定义的行为。你想避免这种情况。

您可以执行以下操作,但在某些平台上可能会失败:

#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
于 2012-10-24T01:37:37.743 回答
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;
于 2012-10-24T05:32:43.403 回答
0

我做错了什么?

uint64_t recover_test = ((a << 32) & b);

a << 32是未定义的行为。

未定义按位左移更多或相同于左操作数的位宽度。

于 2012-10-23T20:58:10.710 回答
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");
}

与所有其他的大部分相同,我只是在任何地方使用它们之前将整数显式转换回无符号整数。如果要么ab最终是负面的,这可能很重要。

输出:

a: 314470568
b: 2100994872
Recovered: 1350640807215539000
Test PASSED

当 b 为负时输出(感谢 Daniel):

a: 314470568
b: 18446744073663062840    # -46488776
Recovered: 1350640809363022648
Test PASSED
于 2012-10-23T21:05:59.013 回答