使用 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_tint64_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++?