快速乘法

时间:2009-10-21 12:34:25

标签: c integer microcontroller multiplication

我正在为具有快速整数运算的微处理器编写代码,并且没有那么快浮点运算。我需要将整数除以1到9之间的数字,并将结果转换回整数。

我制作了一个浮点数组,其成员包括0,1,0.5,0.3333等。 但我认为除了(1/3)之外的数字还有MAGIC常量(如0x55555556)。

这是什么数字?

3 个答案:

答案 0 :(得分:4)

如果微控制器上的除法指令足够快,请使用它。如果你需要结果的小数部分,你可以使用余数;在大多数体系结构中,除法指令将商置于一个寄存器中,将余数置于另一个寄存器中。

如果你的除法指令不够快但是乘法指令是,你可以使用以下技术(听起来好像这是你所追求的技术)。在大多数体系结构中,将32位数乘以另一个32位数会产生64位结果;更重要的一半存储在一个寄存器中,而较不重要的一半存储在另一个寄存器中。您可以通过实现通过数字n的除法与乘以(2 ^ 32)/ n相同,然后获取结果的更重要的32位来利用这一点。换句话说,如果你想除以3,你可以乘以0x100000000 / 3 = 0x55555555,然后取结果的更重要的32位。

你在这里做的实际上是一种定点运算。有关详细信息,请查看Wikipedia article

答案 1 :(得分:1)

我假设,基于微控制器标签,您没有快速整数除法。我的答案也是无符号值 - 它适用于有符号值,你只需要限制下面棘手位中使用的数字。

一个好的开始是除以2,4和8.这些可以分别通过1,2和3位的右移来完成,假设你的CPU有一个逻辑右移指令。

其次,除以1只是按原样保持数字。刚离开,3,5,6,7和9。

Tricky位从这里开始:

对于其他数字,您可以使用以下事实:可以用乘法和移位替换除法。

假设你有一个16位处理器。要除以N,则乘以256 / N并向右移8位:

N = 3, multiply by 85
N = 5, multiply by 51
N = 6, multiply by 43
N = 7, multiply by 37
N = 9, multiply by 28

以72/5的随机示例为例。乘以72乘51得到3672然后向右移8位得到14。

为了使其正常工作,您使用的数字不得溢出16位。由于你的最坏情况是乘以85,你可以处理最多771的数字。

这是有效的原因是因为8位的右移与除以256相同,并且:

  m * (256 /  n) / 256
= m / (n /  256) / 256
= m /  n *  256  / 256
= m /  n * (256  / 256)
= m /  n

如果你有一个32位处理器,值和范围会有所改变,因为它是65536 / N:

N = 3, multiply by 21,846, right shift 16 bits, max value roughly 196,600.
N = 5, multiply by 13,108.
N = 6, multiply by 10,923.
N = 7, multiply by  9,363.
N = 9, multiply by  7,282.

再次,让我们选择随机20,000 / 7:20,000乘以9,363是187,260,000,当你右移16位时,你得到2,857 - 实际结果是2,857。

C中的以下测试程序显示给定值的准确度数字。它使用有符号值,因此最高可达98,000,但您可以看到最大误差为1,它出现在13,110的低点(仅有0.008%误差)。

#include <stdio.h>
int res[5] = {0};
int low[5] = {-1,-1,-1,-1,-1};
int da[] = {3,5,6,7,9};
int ma[] = {21846,13108,10923,9363,7282};
int main (void) {
    int n, i;
    for (n = 0; n < 98000; n++) {
        for (i = 0; i < sizeof(da)/sizeof(da[0]); i++) {
            int r1 = n / da[i];
            int r2 = (n * ma[i])>>16;
            int dif = abs (r1-r2);
            if (dif >= 5) {
                printf ("%d / %d gives %d and %d\n", n, da[i], r1, r2);
                return 1;
            }
            res[dif]++;
            if (low[dif] == -1) {
                low[dif] = n;
            }
        }
    }
    for (i = 0; i < sizeof(res)/sizeof(res[0]); i++) {
        printf ("Difference of %d: %6d, lowest value was %6d\n", i, res[i], low[i]);
    }
    return 0;
}

输出:

Difference of 0: 335874, lowest value was      0
Difference of 1: 154126, lowest value was  13110
Difference of 2:      0, lowest value was     -1
Difference of 3:      0, lowest value was     -1
Difference of 4:      0, lowest value was     -1

答案 2 :(得分:1)

整数常数除以整数常数可以用移位和乘法的组合代替。有关详细信息,请参阅this optimization guide。对于cource来说,如果它在感兴趣的芯片上更快地运行,那么它是有用的。