我编写了一个函数 trailing_zeroes(int n),它返回数字二进制表示中的尾随零的数量。
示例:二进制文件中的4
为100
,因此本例中的函数返回2
。
unsigned trailing_zeroes(int n) {
unsigned bits;
bits = 0;
while (n >= 0 && !(n & 01)) {
++bits;
if (n != 0)
n >>= 1;
else
break;
}
return bits;
}
if
语句的原因是因为如果n
等于0,则会有一个循环。
我觉得这个代码写的很丑陋;有更好的方法吗?
我想避免break
内的while
语句,因为很多人告诉我在while/for
内使用该语句有时候可能是"非正式的" 。我想要改写这样的功能,但我不认为这是最好的方法:
unsigned bits;
if (n == 0)
return bits = 1;
bits = 0;
while (!(n & 01)) {
++bits;
n >>= 1;
}
答案 0 :(得分:4)
您的功能不正确:0
仍有无限循环。测试应该是:
while (n > 0 && !(n & 1))
请注意,您无法使用此方法处理负数,因此您的函数可能应采用unsigned
数字参数,或者您可以将参数转换为unsigned
。
您的函数应该是特殊情况0
并使用更简单的循环:
unsigned trailing_zeroes(int n) {
unsigned bits = 0, x = n;
if (x) {
while ((x & 1) == 0) {
++bits;
x >>= 1;
}
}
return bits;
}
以上功能非常简单易懂。如果结果很小,它会很快。 0
返回的值与函数中的0
一样,这是值得怀疑的,因为0
实际上有unsigned
类型中的值位的尾随零。
有一个更有效的方法,步骤数量恒定:
unsigned trailing_zeroes(int n) {
unsigned bits = 0, x = n;
if (x) {
/* assuming `x` has 32 bits: lets count the low order 0 bits in batches */
/* mask the 16 low order bits, add 16 and shift them out if they are all 0 */
if (!(x & 0x0000FFFF)) { bits += 16; x >>= 16; }
/* mask the 8 low order bits, add 8 and shift them out if they are all 0 */
if (!(x & 0x000000FF)) { bits += 8; x >>= 8; }
/* mask the 4 low order bits, add 4 and shift them out if they are all 0 */
if (!(x & 0x0000000F)) { bits += 4; x >>= 4; }
/* mask the 2 low order bits, add 2 and shift them out if they are all 0 */
if (!(x & 0x00000003)) { bits += 2; x >>= 2; }
/* mask the low order bit and add 1 if it is 0 */
bits += (x & 1) ^ 1;
}
return bits;
}
请注意,我们可以通过将第一步更改为
来处理任何更大的int
尺寸
while (!(x & 0x0000FFFF)) { bits += 16; x >>= 16; }
某些编译器具有内置函数__builtin_ctz()
,可使用非常高效的汇编代码计算尾随零的数量。它不是C标准功能,但是以降低可移植性为代价,如果可用,您可能希望使用它。查看编译器的文档。
以下是GCC docuemntation的摘要:
内置函数:
int __builtin_ctz (unsigned int x)
从最低有效位开始,返回
x
中的尾随0位数。如果x
为0
,则结果未定义。
答案 1 :(得分:3)
正如已经提到的,有一个内置可以做到这一点,因为它可能使用硬件,它可能非常快。但是,doc for GCC确实表示如果输入为0,结果是未定义的。因为这是一个扩展,它可能不适用于您的编译器。
否则,每当有人说'但操纵'时,或者'比特计算',您需要找到"Hacker's Delight"的副本。一本书很好,我买了两个版本。大约有4页(第1版)致力于此,' ntz' (尾随零的数量)。如果你已经有了一个&nbspz  (前导零的数量)或者“前导零”的数量。功能,然后你可以直接获得ntz。否则本书给出several implementations,有些使用popcnt,有一个有循环,其他使用二进制搜索。
例如,
int ntz3(unsigned x) {
int n;
if (x == 0) return(32);
n = 1;
if ((x & 0x0000FFFF) == 0) {n = n +16; x = x >>16;}
if ((x & 0x000000FF) == 0) {n = n + 8; x = x >> 8;}
if ((x & 0x0000000F) == 0) {n = n + 4; x = x >> 4;}
if ((x & 0x00000003) == 0) {n = n + 2; x = x >> 2;}
return n - (x & 1);
}