逐位除以2的倍数

时间:2013-06-16 15:58:55

标签: c bit-manipulation

我发现很多关于按位划分的帖子,我完全理解大多数按位使用,但我想不出具体的划分。我想将给定数字(比方说100)除以2的所有倍数(注意:我不想用2位倍数的幂除以!)
例如:100 / 2,100 / 4,100 / 6,100 / 8,100 / 10 ... 100/100
另外我知道因为使用unsigned int答案将被舍入,例如100/52 = 0,但这并不重要,因为我可以跳过这些答案或打印它们,没问题。我关心的主要是如何划分6或10等等(2的倍数)。需要在C中完成它,因为我可以设法将你给我的任何代码从Java转换为C.

3 个答案:

答案 0 :(得分:2)

根据division by 3 question的已接受解决方案显示的数学运算,您可以导出除法算法的重复:

计算(int)(X / Y)

  • 令k为2 k ≥Y且2 k-1 < ÿ
    (注意,2 k = (1 << k)
  • 设d = 2 k - Y
  • 然后,如果A = (int)(X / 2 k )且B = X%2 k

    X = (1 << k) * A + B
      = (1 << k) * A - Y * A + Y * A + B
      = d * A + Y * A + B
      = Y * A + (d * A + B)
    
  • 因此,

    X/Y = A + (d * A + B)/Y
    

换句话说,

  

如果S(X, Y) := X/Y,则S(X, Y) := A + S(d * A + B, Y)

这种重复可以通过简单的循环实现。循环的停止条件是当分子低于2 k 时。函数divu仅使用按位运算符和使用无符号类型来实现重复。数学运算的辅助函数未实现,但不应太难(链接的答案已提供完整的add实现)。 rs()函数用于“右移”,它在unsigned输入上签名扩展。函数divint的实际API,在委托给y之前检查除以零和负divunegate做2的补语否定。

static unsigned divu (unsigned x, unsigned y) {
    unsigned k = 0;
    unsigned pow2 = 0;
    unsigned mask = 0;
    unsigned diff = 0;
    unsigned sum = 0;
    while ((1 << k) < y) k = add(k, 1);
    pow2 = (1 << k);
    mask = sub(pow2, 1);
    diff = sub(pow2, y);
    while (x >= pow2) {
        sum = add(sum, rs(x, k));
        x = add(mul(diff, rs(x, k)), (x & mask));
    }
    if (x >= y) sum = add(sum, 1);
    return sum;
}

int div (int x, int y) {
    assert(y);
    if (y > 0) return divu(x, y);
    return negate(divu(x, negate(y)));
}

此实现取决于signed int使用2的补码。为了获得最大的可移植性,div应该在调用divu之前将负参数转换为2的补码。然后,它应该将divu的结果从2的补码转换回本地签名的表示。

答案 1 :(得分:0)

尽可能使用运算符/进行整数除法。

例如,当您想要将100除以6或10时,您应该写100/6100/10。 当你提到位智能除法时,你(1)是指运算符/的实现还是(2)你指的是除以2的幂的除法。

对于(1),处理器应具有整数除法单位。如果不是,编译器应提供良好的实现。

对于(2),您可以使用100>>2代替100/4。如果在编译时知道分子,那么一个好的编译器应该自动使用移位指令。

答案 2 :(得分:0)

以下代码适用于正数。当被除数或除数或两者都是负数时,有标志来适当地改变答案的符号。

int divi(long long m, long long n)
{
    if(m==0 || n==0 || m<n)
        return 0;
    long long  a,b;
    int f=0;
    a=n;b=1;

    while(a<=m)
    {
        b = b<<1;
        a = a<<1;
        f=1;
    }

    if(f)
    {
        b = b>>1;
        a = a>>1;
    }

    b = b + divi(m-a,n);
    return b;
}