[英]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.