繁体   English   中英

使用C在定点算法中使用32位数据进行64位操作

[英]Doing 64 bit manipulation using 32 bit data in Fixed point arithmetic using C

我陷入了一个问题。 我正在仅支持32位操作的硬件上工作。

sizeof(int64_t) is 4. Sizeof(int) is 4.  

我正在移植一个应用程序,该应用程序假定int64_t的大小为8个字节。 问题是它具有此宏BIG_MULL(a,b)((int64_t)(a)*(int64_t)(b)>> 23)

结果始终是32位整数,但是由于我的系统不支持64位操作,因此它总是向我返回该操作的LSB,所有结果的取整使我的系统崩溃。

有人可以帮我吗?

此致Vikas Gupta

您根本无法可靠地以32位整数存储64位数据。 您要么必须重新设计软件,以使用32位整数作为最大可用大小,要么提供一种为64位整数提供64位存储的方法。 都不是简单的-要礼貌。

创建一种结构是一种可能的方法-不是一件容易的事:

typedef struct { uint32_t msw; uint32_t lsw; } INT64_t;

然后,您可以将数据存储在两个32位整数中,并对结构的组成部分进行算术运算。 当然,一般来说,32位乘32位的乘法会产生64位的答案。 要进行完全乘法运算而不会溢出,您可能被迫存储4个16位无符号数字(因为可以乘以16位数字以产生32位结果而不会产生溢出)。 您将使用函数来完成艰苦的工作-因此,该宏将成为对一个函数的调用,该函数接受两个INT64_t结构(指向?)并返回一个。

它不会像以前那样快...但是,如果他们在必要的地方都使用宏,它就有一定的工作机会。

我假设您要相乘的数字是32位整数。 您只想生成一个大于32位的乘积。 然后,您要从产品中删除一些已知数量的最低有效位。

首先,这会将两个整数相乘并溢出。

#define WORD_MASK ((1<<16) - 1)
#define LOW_WORD(x)  (x & WORD_MASK) 
#define HIGH_WORD(x) ((x & (WORD_MASK<<16)) >> 16)
#define BIG_MULL(a, b) \
    ((LOW_WORD(a)  * LOW_WORD(b))  <<  0) + \
    ((LOW_WORD(a)  * HIGH_WORD(b)) << 16) + \
    ((HIGH_WORD(a) * LOW_WORD(b))  << 16) + \
    ((HIGH_WORD(a) * HIGH_WORD(b)) << 32)

如果要从中删除23个最低有效位,则可以像这样调整它。

#define WORD_MASK ((1<<16) - 1)
#define LOW_WORD(x)  (x & WORD_MASK) 
#define HIGH_WORD(x) ((x & (WORD_MASK<<16)) >> 16)
#define BIG_MULL(a, b) \
    ((LOW_WORD(a)  * HIGH_WORD(b)) >> 7) + \
    ((HIGH_WORD(a) * LOW_WORD(b))  >> 7) + \
    ((HIGH_WORD(a) * HIGH_WORD(b)) << 9)

请注意,如果乘法的实际乘积大于41(= 64-23)位,这仍然会溢出。


更新:

我已经调整了代码以处理有符号整数。

#define LOW_WORD(x)  (((x) << 16) >> 16) 
#define HIGH_WORD(x) ((x) >> 16)
#define ABS(x) (((x) >= 0) ? (x) : -(x))
#define SIGN(x) (((x) >= 0) ? 1 : -1)
#define UNSIGNED_BIG_MULT(a, b) \
    (((LOW_WORD((a))  * HIGH_WORD((b))) >> 7) + \
     ((HIGH_WORD((a)) * LOW_WORD((b)))  >> 7) + \
     ((HIGH_WORD((a)) * HIGH_WORD((b))) << 9))
#define BIG_MULT(a, b) \
    (UNSIGNED_BIG_MULT(ABS((a)), ABS((b))) * \
     SIGN((a)) * \
     SIGN((b)))

如果您将宏更改为

#define  BIG_MULL(a,b) ( (int64_t)(a) * (int64_t)(b))

因为它看起来像为您定义了int64_t它应该工作

尽管sizeof(int64_t) == 4提出了其他问题,但这是错误的:

#define BIG_MULL(a,b) ( (int64_t)(a) * (int64_t)(b) >> 23)

如果平台支持,则标准要求intN_t类型的N = intN_t和64...。

您应该使用的类型是intmax_t ,它被定义为平台支持的最大整数类型。 如果您的平台没有64位整数,则代码不会因intmax_t而中断。

您可能想要查看一个bignum库,例如GNU GMP 从某种意义上说,bignum库是多余的,因为它们通常支持任意大小的数字,而不仅仅是增加固定大小的数字。 但是,由于它已经完成,因此它做得比您想做的要多的事实可能不是问题。

另一种方法是将两个32位int打包到类似于Microsoft LARGE_INTEGER的结构中:

typedef union _LARGE_INTEGER {
    struct {
        DWORD LowPart;
        LONG HighPart;
    };
    struct {
        DWORD LowPart;
        LONG HighPart;
    } u;
    LONGLONG QuadPart;
} LARGE_INTEGER;

并创建接受此类型参数并返回此类型结构结果的函数。 您也可以将这些操作包装在C ++类中,该类将允许您定义运算符重载,使表达式看起来更自然。 但是,我将看一下已经制作的库(例如GMP),看看它们是否可以使用-可以节省很多工作。

我只是希望您不需要在直的C语言中使用像这样的结构来实现除法-这很痛苦而且运行缓慢。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM