我发现很多关于按位划分的帖子,我完全理解大多数按位使用,但我想不出具体的划分。我想将给定数字(比方说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.
答案 0 :(得分:2)
根据division by 3 question的已接受解决方案显示的数学运算,您可以导出除法算法的重复:
计算(int)
(X / Y)
(1 << k)
)然后,如果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
输入上签名扩展。函数div
是int
的实际API,在委托给y
之前检查除以零和负divu
。 negate
做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/6
或100/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;
}