使用 int32_t 访问 int64_t 的高低部分
Accessing hi and low part of int64_t with int32_t
我正在开发 32 位架构,其中 int64_t
仅使用最新版本的编译器(软件仿真)定义。由于我们没有完全升级到上一个编译器,我想用一个联合来管理64位整数并定义基本的算术运算。
我写了这个:
typedef union _int64_u {
int64_t int64;
int64_32 int32;
} int64_u;
typedef struct _int64_32 {
int32_t hi;
int32_t lo;
}
我想澄清以下几点:
- 这种定义(部分和类型)的命名标准是什么?
- 这个解决方案正确吗?
以下是加减函数的例子:
#pragma inline
#pragma always_inline
int64_u int64_sub(int64_u x, int64_u y)
{
int64_u n;
asm("%0 = %1 - %2;"
: "=d" (n.int32.lo)
: "d" (x.int32.lo), "d" (y.int32.lo));
asm("%0 = %1 - %2 + CI - 1;"
: "=d" (n.int32.hi)
: "d" (x.int32.hi), "d" (y.int32.hi));
return n;
}
#pragma inline
#pragma always_inline
int64_u int64_add(int64_u x, int64_u y)
{
int64_u n;
asm("%0 = %1 + %2;"
: "=d" (n.int32.lo)
: "d" (x.int32.lo), "d" (y.int32.lo));
asm("%0 = %1 + %2 + CI;"
: "=d" (n.int32.hi)
: "d" (x.int32.hi), "d" (y.int32.hi));
return n;
}
也许我误解了这些限制,但为什么不这样做呢?
#ifdef HAS_INT64_T
typedef int64_t int64;
inline int64 int64_sub(int64 x, int64 y) {
return x - y;
}
...
#else /* !HAS_INT64_T */
typedef struct int64 { int32_t hi, lo; } int64;
inline int64 int64_sub(int64 x, int64 y) {
*ASM*
}
...
#endif /* !HAS_INT64_T */
当然,您必须小心字节顺序等。
如果您的意思是您的编译器支持 int64_t
但不支持对它们的某些操作,那么您可以按照您的建议使用 union
或定义一些简单的转换函数。我会调用 int32_t
组件的结构 parts 等。
(还值得注意的是 int32_t
和 int64_t
重叠并写入其中一个并从另一个读取不是严格的别名安全,尽管它在实践中往往会起作用。 )
首先我应该注意到 int64_t
是 C99 的一个特性,但旧的 C89 编译器通常已经通过一些扩展类型(如 long long
or __int64
. Check if it's the case of your old compiler, if not then check if your compiler has an extension to get the carry flag, like __builtin_addc()
or __builtin_add_overflow()
)支持双字运算。如果全部失败则进入下一步
现在%0 = %1 + %2;
在我所知道的任何体系结构中都不是汇编指令,但它看起来比传统的助记语法更具可读性。但是 您甚至不需要像这样对多词 additions/subtractions 使用汇编。因为
直接在C中做很简单
- 2 的补码 don't depend on the signness of the type 和
中的基本运算
- 如果发生溢出,那么结果将小于我们可以用来获取进位位的操作数(无符号)
关于实现,因为你的旧编译器没有 64 位类型,所以没有必要声明联合,你也不能这样做,因为之前没有声明 int64_t
。您可以将整个事物作为结构访问。
#if COMPILER_VERSION <= SOME_VERSION
typedef UINT64_T {
uint32_t h;
uint32_t l;
} uint64_t;
uint64_t add(uint64_t x, uint64_t y)
{
uint64_t z;
z.l = x.l + y.l; // add the low parts
z.h = x.h + y.h + (z.l < x.l); // add the high parts and carry
return z;
}
// ...
#else
uint64_t add(uint64_t x, uint64_t y)
{
return x + y;
}
#endif
t = add(2, 3);
如果您需要有符号类型,则需要稍作改动
typedef INT64_T {
int32_t h;
uint32_t l;
} int64_t;
add/sub/mul功能与未签名版本一样
聪明的现代编译器会识别 z.l < x.l
模式并在具有它们的体系结构中变成 add/adc
对,因此那里没有比较 and/or 分支。如果不是那么不幸的是你仍然需要退回到内联汇编
另见
- Multiword addition in C
- Access the flags without inline assembly?
- Efficient 128-bit addition using carry flag
- An efficient way to do basic 128 bit integer calculations in C++?
我正在开发 32 位架构,其中 int64_t
仅使用最新版本的编译器(软件仿真)定义。由于我们没有完全升级到上一个编译器,我想用一个联合来管理64位整数并定义基本的算术运算。
我写了这个:
typedef union _int64_u {
int64_t int64;
int64_32 int32;
} int64_u;
typedef struct _int64_32 {
int32_t hi;
int32_t lo;
}
我想澄清以下几点:
- 这种定义(部分和类型)的命名标准是什么?
- 这个解决方案正确吗?
以下是加减函数的例子:
#pragma inline
#pragma always_inline
int64_u int64_sub(int64_u x, int64_u y)
{
int64_u n;
asm("%0 = %1 - %2;"
: "=d" (n.int32.lo)
: "d" (x.int32.lo), "d" (y.int32.lo));
asm("%0 = %1 - %2 + CI - 1;"
: "=d" (n.int32.hi)
: "d" (x.int32.hi), "d" (y.int32.hi));
return n;
}
#pragma inline
#pragma always_inline
int64_u int64_add(int64_u x, int64_u y)
{
int64_u n;
asm("%0 = %1 + %2;"
: "=d" (n.int32.lo)
: "d" (x.int32.lo), "d" (y.int32.lo));
asm("%0 = %1 + %2 + CI;"
: "=d" (n.int32.hi)
: "d" (x.int32.hi), "d" (y.int32.hi));
return n;
}
也许我误解了这些限制,但为什么不这样做呢?
#ifdef HAS_INT64_T
typedef int64_t int64;
inline int64 int64_sub(int64 x, int64 y) {
return x - y;
}
...
#else /* !HAS_INT64_T */
typedef struct int64 { int32_t hi, lo; } int64;
inline int64 int64_sub(int64 x, int64 y) {
*ASM*
}
...
#endif /* !HAS_INT64_T */
当然,您必须小心字节顺序等。
如果您的意思是您的编译器支持 int64_t
但不支持对它们的某些操作,那么您可以按照您的建议使用 union
或定义一些简单的转换函数。我会调用 int32_t
组件的结构 parts 等。
(还值得注意的是 int32_t
和 int64_t
重叠并写入其中一个并从另一个读取不是严格的别名安全,尽管它在实践中往往会起作用。 )
首先我应该注意到 int64_t
是 C99 的一个特性,但旧的 C89 编译器通常已经通过一些扩展类型(如 long long
or __int64
. Check if it's the case of your old compiler, if not then check if your compiler has an extension to get the carry flag, like __builtin_addc()
or __builtin_add_overflow()
)支持双字运算。如果全部失败则进入下一步
现在%0 = %1 + %2;
在我所知道的任何体系结构中都不是汇编指令,但它看起来比传统的助记语法更具可读性。但是 您甚至不需要像这样对多词 additions/subtractions 使用汇编。因为
- 2 的补码 don't depend on the signness of the type 和 中的基本运算
- 如果发生溢出,那么结果将小于我们可以用来获取进位位的操作数(无符号)
关于实现,因为你的旧编译器没有 64 位类型,所以没有必要声明联合,你也不能这样做,因为之前没有声明 int64_t
。您可以将整个事物作为结构访问。
#if COMPILER_VERSION <= SOME_VERSION
typedef UINT64_T {
uint32_t h;
uint32_t l;
} uint64_t;
uint64_t add(uint64_t x, uint64_t y)
{
uint64_t z;
z.l = x.l + y.l; // add the low parts
z.h = x.h + y.h + (z.l < x.l); // add the high parts and carry
return z;
}
// ...
#else
uint64_t add(uint64_t x, uint64_t y)
{
return x + y;
}
#endif
t = add(2, 3);
如果您需要有符号类型,则需要稍作改动
typedef INT64_T {
int32_t h;
uint32_t l;
} int64_t;
add/sub/mul功能与未签名版本一样
聪明的现代编译器会识别 z.l < x.l
模式并在具有它们的体系结构中变成 add/adc
对,因此那里没有比较 and/or 分支。如果不是那么不幸的是你仍然需要退回到内联汇编
另见
- Multiword addition in C
- Access the flags without inline assembly?
- Efficient 128-bit addition using carry flag
- An efficient way to do basic 128 bit integer calculations in C++?