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

时间:2010-04-18 04:42:52

标签: c++ 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,舍入所有结果使我的系统崩溃。

有人能帮助我吗?

此致 维卡斯古普塔

5 个答案:

答案 0 :(得分:7)

您无法在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结构的两个(指向?)并返回一个。

它不会像以前那么快......但如果他们在任何地方使用宏,它就有可能工作。

答案 1 :(得分:3)

我假设您尝试乘以的数字是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)))

答案 2 :(得分:0)

如果您将宏更改为

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

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

答案 3 :(得分:0)

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

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

标准要求intN_t类型的值为N = 8,16,32和64 ... 如果平台支持它们。

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

答案 4 :(得分:0)

您可能希望查看一个bignum库,例如GNU GMP。在某种意义上,bignum库是过度的,因为它们通常支持任意大小的数字,而不仅仅是增加固定大小的数字。但是,由于它已经完成,它的功能超出了你想要的事实可能不是问题。

另一种方法是将一些32位整数打包到类似于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中使用这样的结构实现除法 - 这很痛苦并且运行缓慢。