完全に移植可能な方法でこれを行うことができない場合があります。
その理由は、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