2

1uint64_t originalつと 2 つの通常の 4 バイト整数 (符号付き) がある場合、値を 2 つの整数に格納し、後で符号なし 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をシフトすると、値が十分に大きくないため、値がすべて左に「ドロップオフ」します。また、2つの部分を一緒にORするのではなく、ANDすることもできました。

于 2012-10-23T21:00:38.947 に答える
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 に答える
1

このような:

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

最初にaを64ビット数にプロモートするようにコンパイラーに指示する必要があります-次にシフトを実行します。それ以外の場合は、32ビット値の内部にシフトします-上位ビットをダンプします。

@編集:うわー。したがって、bが署名されている場合、| 符号付き32ビット値を使用すると、符号付きビットがbの先頭から、64ビットにキャストされた後のaの先頭ビットに昇格します。したがって、|の前に最初にbをunsignedにキャストする必要があります。

于 2012-10-23T20:58:36.843 に答える
1

完全に移植可能な方法でこれを行うことができない場合があります。

その理由は、N ビットの符号付き int は 2 N -1 の異なる値を表すのに十分である可能性があるためです。

これは、符号付き整数が符号と大きさまたは 1 の補数表現である場合に特に当てはまります。これらの表現は、0 を中心に対称です。

2 の補数表現でさえ、0 を中心に対称であり、2 N を許可する代わりに、-(2 N-1 -1) から 2 N-1 -1 までの 2 N -1 の異なる値のみを許可する場合があります(上記場合同様)。 -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
于 2012-10-24T01:37:37.743 に答える
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");
}

他のすべてのものとほとんど同じですが、int をどこでも使用する前に明示的に unsigned int に戻すだけです。aまたはいずれかbが負になる場合、これは重要になる可能性があります。

出力:

a: 314470568
b: 2100994872
Recovered: 1350640807215539000
Test PASSED

b が負の場合の出力 (ダニエルに感謝):

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