【问题标题】:Accessing hi and low part of int64_t with int32_t使用 int32_t 访问 int64_t 的高低部分
【发布时间】:2015-03-03 09:56:06
【问题描述】:

我正在开发一个 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;
}  

我想澄清以下几点:

  • 这种定义(部件和类型)的命名标准是什么?
  • 这个解决方案正确吗?

以下是 add 和 sub 函数的示例:

#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;
} 

【问题讨论】:

    标签: c struct 64-bit unions int64


    【解决方案1】:

    首先我应该注意到int64_t 是一个 C99 功能,但旧的 C89 编译器通常已经通过一些扩展类型(如 long long__int64)支持双字操作。检查您的旧编译器是否是这种情况,如果不是,则检查您的编译器是否有扩展来获取进位标志,例如__builtin_addc() or __builtin_add_overflow()。如果都失败了,进入下一步

    现在%0 = %1 + %2; 在我所知道的任何架构中都不是汇编指令,但它看起来比传统的助记符语法更具可读性。然而你甚至不需要使用汇编来进行这样的多字加法/减法。直接在 C 中执行非常简单,因为

    关于实现,由于您的旧编译器没有 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 &lt; x.l 模式并在具有它们的架构中转换为add/adc 对,因此那里没有比较和/或分支。如果不是,那么不幸的是,您仍然需要退回到内联汇编

    另见

    【讨论】:

    • 看看 SHARC 程序集。 %0 = %1 + %2; 是一个汇编宏声明
    • 我认为我不能声明自己的int64_t,因为后缀_t 是POSIX 保留的。
    • 我试过(z.l &lt; x.l),但我的编译器不够聪明。它做了一个测试。
    • 你在哪里读到可以声明一个int64_t,其中存储不是64位?
    • 该代码有问题,因为它假定了无符号比较(并且还需要-fwrapv 以确保安全)。添加0...01...1 (-1) 将给出比较结果(-1 ((uint32_t)z.l < (uint32_t)x.l) 可能足以使其正常工作。可以稍微清理一下。
    【解决方案2】:

    也许我误解了这些限制,但为什么不做下面的事情呢?

    #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_ts 但不支持对它们的某些操作,那么您可以按照您的建议使用union 或定义一些简单的转换函数。我会为 int32_t 组件 parts 等调用结构。

    (同样值得注意的是,int32_tint64_t 重叠并写入其中一个并从另一个读取不是严格混叠安全的,尽管它在实践中往往会起作用。)

    【讨论】:

    • 因为旧版编译器我无法访问int64_t
    • @coin 这难道不会阻止您将int64_t 加入工会吗?如果可行,在旧版本的编译器上根本不使用int64_t 似乎更安全,只需创建一个等效项,就像在这个例子中一样。对于新编译器,您将使用 -DHAS_INT64_T 进行编译,而对于旧编译器则不使用它(或者使用编译器提供的任何方便的宏来确定版本)。 structs 可以按值传递和返回。
    猜你喜欢
    • 2015-09-22
    • 1970-01-01
    • 2016-09-02
    • 2016-12-16
    • 1970-01-01
    • 1970-01-01
    • 2013-05-26
    • 2019-01-25
    • 1970-01-01
    相关资源
    最近更新 更多