是否可以在不使用专用功能的情况下反转以2为底的变量的移位操作?我正在寻找变量y。 x和z是已知的。 示例:
x << y = z
1 << 6 = 64
y = 64 ???
有很多解决方案,包括log(64)/ log(2),但是我将不得不使用math.h,但是我正在寻找某种按位运算。 谢谢。
编辑:谢谢您的回答!我的问题得到了回答,用一个简单的操作就不可能做到这一点,我的编译器没有CTZ。 BR
答案 0 :(得分:1)
假设您从x
知道z
和z = x << y
,并想使用按位运算找出y
,一种简单的方法是取x
并一次将其移位一位,将结果与z
进行比较(即,从y = 0
开始,并在每次比较失败后将y
递增1)。
edit :另一方面,如果x
为1
(或者实际上x
的最低位设置),您也可以计算z
中的尾随零。为此,您的处理器可能只有一条指令,您的C编译器可能会将其公开为不可移植的扩展名,例如__builtin_ctz
(您可以考虑使用预处理器在此和便携式解决方案之间进行切换)。对于这种问题,还有比平凡的循环更快,更便携的解决方案–搜索“计数尾随零”。
(在x
没有设置最低位的情况下,可能会计算x
和z
中的尾随零以找出差异。)
答案 1 :(得分:1)
标准C中没有单个操作或函数可以计算整数中的尾随零位的数目。正如其他答案所建议的,您可以通过循环一次检查一位来执行这样的计算。但是,如果您需要做很多事情,那么您可能想要一个更有效的选择,例如:
int trailing_zero_bits(uint64_t x) {
uint64_t bits = ~(uint64_t)0;
int rval = 0;
for (int shift = 32; shift; shift >>= 1) {
bits >>= shift;
if (!(x & bits)) {
rval += shift;
x >>= shift;
}
}
return rval + !x; // The !x adds 1 if x is zero at this point
}
对于64位一次转换,该循环将精确地进行6次迭代,相对于 多达63次。
当然,可能存在特定于系统或环境的替代方案,其效率更高。
答案 2 :(得分:0)
替代功能的想法可以使用switch语句,如下所示:
int trailing_zero_bits(uint64_t x) {
int iRetVal = 0;
switch (x & 0x3f) {
case 2:
iRetVal = 1;
break;
case 4:
iRetVal = 2;
break;
case 8:
iRetVal = 3;
break;
case 16:
iRetVal = 4;
break;
case 32:
iRetVal = 5;
break;
case 64:
iRetVal = 6;
break;
default:
iRetVal = 0;
}
return iRetVal;
}
这可以使用Duff's Device压缩为:
int trailing_zero_bits(uint64_t x) {
int iRetVal = 0;
switch (x & 0x3f) {
case 64: iRetVal++;
case 32: iRetVal++;
case 16: iRetVal++;
case 8: iRetVal++;
case 4: iRetVal++;
case 2: iRetVal++;
break;
default:
break;
}
return iRetVal;
}
或者您可以采用如下查找表的方法:
int trailing_zero_bits(uint64_t x) {
unsigned char x1[9] = { 0, 0, 1, 0, 2, 0, 0, 0, 3 };
unsigned char x2[9] = { 0, 4, 5, 0, 6, 0, 0, 0, 0 };
return x1[x & 0xf] | x2[(x & 0x30) >> 4];
}