通过16位移位进行32位乘法运算

时间:2014-09-08 04:33:21

标签: c assembly bit-manipulation multiplication bit-shift

我正在使用移位和加法编写一个软乘法函数调用。现有的函数调用如下:

unsigned long __mulsi3 (unsigned long a, unsigned long b) {

    unsigned long answer = 0;

    while(b)
    {
        if(b & 1) {
            answer += a;
        };

        a <<= 1;
        b >>= 1;
    }
    return answer;
}

虽然我的硬件没有倍增器,但我有一个硬移位器。移位器一次最多可以移位16位。

如果我想充分利用我的16位移位器。有关如何调整上述代码以反映我的硬件功能的任何建议?给定的代码每次迭代仅移位1位。

16位移位器可以一次将32位无符号长值移位16个位置。 sizeof(无符号长整数)== 32位

4 个答案:

答案 0 :(得分:1)

使用16位移位可以帮助您使用以下方法进行小幅度的增强:

(U1 * P + U0) * (V1 * P + V0) =
= U1 * V1 * P * P + U1 * V0 * P + U0 * V1 * P + U0 * V0 =
= U1 * V1 * (P*P+P) + (U1-U0) * (V0-V1) * P + U0 * V0 * (1-P)

如果P是2的便利功率(例如,2 ** 16,2 ** 32),那么乘以它就是快速移位。这减少了从4到3的较小数字的乘法,并且递归地,O(N **(3/2))而不是O(N ** 2)对于非常长的数字。

至少在Knuth的TAoCP中描述了这种方法。还有更多高级版本。

对于小数字(例如8乘8位),如果你有足够的快速ROM,下面的方法很快:

a * b = square(a+b)/4 - square(a-b)/4

如果要列表int(square(x)/4),则无符号乘法需要1022个字节,签名一个需要510个字节。

答案 1 :(得分:0)

上面的代码正在以传统方式,即我们在小学学习的方式成倍增加:

EX:

    0101
  * 0111
  -------
    0101
   0101.
  0101..
 --------
  100011
当然,如果你没有乘法运算符或1位移位器,你就无法接近它! 但是,您可以通过其他方式执行此操作,例如循环:

unsigned long _mult(unsigned long a, unsigned long b)
{
    unsigned long res =0;

    while (a > 0)
    {
        res += b;
        a--;
    }

    return res;
} 

它很实用,但它可以满足您的需求,无论如何,如果您有更多限制(如计算时间......),您可以考虑其他方法。

答案 2 :(得分:0)

基本方法是(假设换1): -

  • 移开前16位
  • 将前16位的最低位设置为最低16位的最高位
  • 移动底部的16位

取决于你的硬件...

但你可以试试: -

  • 假设unsigned long是32位
  • 假设Big Endian

然后: -

 union Data32
        {
           unsigned long l;
           unsigned short s[2];
        }; 

unsigned long shiftleft32(unsigned long valueToShift, unsigned short bitsToShift)
{
    union Data32 u;
    u.l  = valueToShift
    u.s[0] <<= bitsToShift;
    u.s[0] |= (u.s[1] >> (16 - bitsToShift);
    u.s[1] <<= bitsToShift

    return u.l;
}

然后反向移动

答案 3 :(得分:0)

移位多位的能力不会有多大帮助,除非你有硬件乘法,比如说8位x 8位,或者你可以买一些RAM / ROM来做(如)4位乘4位乘以查找。

通过交换参数以使乘数更小,可以帮助直接转换和添加(正如您所做)。

如果您的机器通常更快地做16位的事情,那么就要对待32位&#39; a&#39; as&#39; a1:a0&#39;一次16位,类似地&#39; b&#39;,你可能能够在相同的一些周期。你的结果只有32位,所以你不需要做&#39; a1 * b1&#39; - 虽然其中一个或两个可能为零,但胜利可能不大!此外,您只需要16位的&#39; a0 * b1&#39;,因此可以完全16位完成 - 但如果b1(假设b <= a)通常为零,则不是也是一场大胜利。对于&#39; a * b0&#39;,您需要一个32位&#39; a&#39;并且32位添加到&#39;回答&#39;,但您的乘数仅为16位...这可能有助于也可能没有帮助。

跳过乘数零运行可能有所帮助 - 取决于处理器和乘数的任何属性。

FWIW:做魔术&#39; a1 * b1&#39;,&#39;(a1-a0)*(b0-b1)&#39;,&#39; a0 * b0&#39;在我的小经验中,通过轮班,加法和减法将结果结合起来是一场绝对的噩梦... ......(a1-a0)&#39;&#39;(b0-b1)& #39;并且他们的产品必须受到尊重,这使得看起来像一个可爱的伎俩有点混乱。当你完成它以及添加和减去时,你必须有一个强大的缓慢乘法,以使它全部值得!当乘以非常非常长的整数时,这可能会有所帮助......但是内存问题可能占主导地位...当我尝试它时,它会令人失望。