【问题标题】:32-bit signed integer multiplication without using 64-bit data type不使用 64 位数据类型的 32 位有符号整数乘法
【发布时间】:2014-05-15 18:19:53
【问题描述】:

我想在不使用 64 位数据类型的情况下进行 32 位有符号整数乘法。我的输入采用 Q1.31(两者)格式。

input1 = A32 (Ah Al) - higher, lower half's of A32
input2 = B32 (Bh Bl) - higher, lower half's of B32

结果应该是 Q1.31 格式,留下溢出的情况。

我需要 C 代码。请同时提供格式说明。

【问题讨论】:

  • 无论如何,将 Ah 和 Bh 相乘,然后左移正确的数量,将小数点移到正确的位置。然后将 Al 和 Bl 相乘,并向右移动正确的数量以将小数点移到正确的位置。然后将这两个加在一起。基本上,基本的乘法就像在纸上做一样。

标签: c integer bit-manipulation multiplication signed


【解决方案1】:

有符号 Q1.31 格式是一种完全小数格式,能够表示介于 -1 和几乎 +1 之间的操作数。比例因子为 231。这意味着当每个 Q1.31 操作数存储在一个 32 位有符号整数中时,我们可以通过计算有符号整数的全双宽度积来生成 Q1.31 乘积,然后将结果右移 31 位。右移是必要的,因为该乘积包含两次比例因子,并且该移位充当删除比例因子的一个实例的除法。

我们可以通过分别计算全积的高低 32 位来计算两个 32 位整数的双倍宽度积。低 32 位被计算为两个输入的普通乘积。要计算高 32 位,我们需要编写一个函数mul32hi()。为了避免在中间计算中使用更宽的类型(即使用超过 32 位的类型),我们需要将原始操作数分成两半,计算它们的部分积,然后对这些部分积进行适当的求和。

请注意,各种处理器都提供了实现mul32hi() 功能的硬件指令。在这种情况下,人们可能希望使用适当的内在函数,或者如果不存在内在函数,则使用一些内联汇编代码,而不是使用此处提供的仿真代码。

它有助于首先将问题简化为相应的无符号乘法umul32hi(),然后通过定义 2 的补码表示(在以下 C 代码中假设)从中得出有符号结果:

#include <stdint.h>

/* compute the upper 32 bits of the product of two unsigned 32-bit integers */
uint32_t umul32hi (uint32_t a, uint32_t b)
{
    /* split operands into halves */
    uint32_t al = (uint16_t)a;
    uint32_t ah = a >> 16;
    uint32_t bl = (uint16_t)b;
    uint32_t bh = b >> 16;
    /* compute partial products */
    uint32_t p0 = al * bl;
    uint32_t p1 = al * bh;
    uint32_t p2 = ah * bl;
    uint32_t p3 = ah * bh;
    /* sum partial products */
    uint32_t cy = ((p0 >> 16) + (uint16_t)p1 + (uint16_t)p2) >> 16;
    return p3 + (p2 >> 16) + (p1 >> 16) + cy;
}

/* compute the upper 32 bits of the product of two signed 32-bit integers */
int32_t mul32hi (int32_t a, int32_t b)
{
    return umul32hi (a, b) - ((a < 0) ? b : 0) - ((b < 0) ? a : 0);
}

/* compute the full 64-bit product of two signed 32-bit integers */
void mul32wide (int32_t a, int32_t b, int32_t *rhi, int32_t *rlo)
{
    *rlo = a * b;           /* bits <31:0> of the product a * b */
    *rhi = mul32hi (a, b);  /* bits <63:32> of the product a * b */
}

/* compute the product of two signed Q1.31 fixed-point numbers */    
int32_t mul_q_1_31 (int32_t a, int32_t b)
{
    int32_t hi, lo;
    mul32wide (a, b, &hi, &lo);
    /* Q1.31 is scaled by 2**31, trim out scale factor */
    return (int32_t)(((uint32_t)hi << 1) | ((uint32_t)lo >> 31));
}

我将“离开溢出情况”的请求解释为忽略溢出。因此,将 -1 (0x80000000) 乘以 -1 (0x80000000) 与 mul_q_1_31() 将返回 -1 (0x80000000)。

【讨论】:

  • 不错的解决方案,但我认为您可以将重点放在效率上。我认为你做的乘法比必要的多。当您计算umul32hi 时,您也可以构造lo 而无需执行a*b。我认为只使用三个乘法来创建一个函数umul32hi(uint32_t a, uint32_t b, uint32_t lo) 也会很有趣。
  • @Zboson:rlorhi 的一些计算可以共享,我同意。我不确定您所说的“三乘法”是什么意思您在考虑 Karatsuba 吗?我按照我的方式编写代码,因为在许多平台上mul32hi() 可能只是一个直接映射到硬件指令的内在函数(或者如果存在这样的指令但没有封装它的内在函数,则可以使用内联汇编)。我应该在回答中提到这一点,但忘记了。
  • 不,我说的不是唐叶?我的意思是,如果你已经有了lo,那么你就不需要构造p0 = al * bl。那么你必须以不同的方式计算进位。
  • 你的回答救了我! stackoverflow.com/questions/28807341/…我希望我能再次投票给你。你是怎么想出公式hi -= ((x&lt;0) ? y : 0) + ((y&lt;0) ? x : 0)的?
  • @Zboson:它直接来自二进制补码表示。例如。 32 位整数 -n 和 -m 表示为无符号数 x=2**32-ny=2**32-m。如果你乘以那些你有x*y = 2**64- 2**32*n - 2**32*m + n*m。中间项表示对产品上半部分的必要修正。使用 -1*-1 完成一个简单的示例应该非常有启发性。
【解决方案2】:

以下站点声称提供 ANSI-C 库的 32 位实现,以便在一般情况下使用 Q 数:http://www.ti.com/tool/sprc542

我没有尝试过,也无法保证它会或不会为您做什么,但该站点上有一个链接,您可以在其中索取源代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-25
    • 2011-01-17
    • 2013-06-21
    • 2019-08-15
    相关资源
    最近更新 更多