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

我遇到了问题。 我正在开发一个只支持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) 

对于N = intN_t和64的值,该标准需要intN_t类型… 如果平台支持它们。

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

您可能想要查看一个bignum库,例如GNU GMP 。 从某种意义上说,bignum库是矫枉过正的,因为它们通常支持任意大小的数字,而不仅仅是增加固定大小的数字。 但是,由于它已经完成,因此它的function超出了您的要求可能不是问题。

另一种方法是将一些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中使用这样的结构实现除法 – 这很痛苦并且运行缓慢。