尾随零 - C.

时间:2017-07-23 22:21:06

标签: c bitwise-operators trailing

我需要一个程序,它返回数字的二进制表示中的尾随零的数量。我在网上找到了一个用C编写的函数,但我不明白它是如何工作的

这是功能:

unsigned tzr(unsigned x) 
{
    unsigned n; /* number of bits */

    n = 0;
    if (!(x & 0x0000FFFF)) { n += 16; x >>= 16; }
    if (!(x & 0x000000FF)) { n +=  8; x >>=  8; }
    if (!(x & 0x0000000F)) { n +=  4; x >>=  4; }
    if (!(x & 0x00000003)) { n +=  2; x >>=  2; }

    n += (x & 1) ^ 1; // anyway what does this do ? 

    return n;
}

现在我真的试着理解它是如何工作的但是我没有得到它。 我真的需要一个可以向我解释的人,我发现这段代码非常复杂。

关于那些十六进制常量,这些是它们的值:

0x0000FFFF = 65535
0x000000FF = 255
0x0000000F = 15
0x00000003 = 3

现在,为什么程序使用这些值并使用数字进行按位AND?

然后我知道如果你想要处理大数字,你必须要 使用while代替第一个if语句,如下所示:

while (!(x & 0x0000FFFF)) { bits += 16; x >>= 16; } // why should I need this ?

但我不知道为什么!在这种情况下使用while而不是if的区别是什么?

5 个答案:

答案 0 :(得分:1)

十六进制常量与值进行“与”运算,以检查最后[数字]数字是否为零。0x0000FFFF是一个二进制数为16的数字。如果与0x0000FFFF的AND值一起等于0,则表示最后16位为零(if s检查该语句的反向)。进一步0x000000FF是一个包含8个二进制数的数字。下一次检查是最后8位数,下一位是2位数,最后一位是2位数,0x00000003是二进制11。在检查之后,数字被移位以检查其他数字是否也为零。这样我们可以检查任意数量的尾随零,因为值是2的幂,并且添加它们就像使用二进制一样。

最后一个语句检查完所有前一个移位后的最后一个数字 - 和1并用XOR(^)检查它是0还是1。

该程序用32位检查数字。您可以将第一个if更改为while以检查更大,例如64位,数字。另一种方法是检查0xFFFFFFFF然后立即移位32位。

答案 1 :(得分:0)

n += (x & 1) ^ 1检查x的当前状态的最低有效位(LSB)。如果LSB是1,那么(x& 1)yeilds 1然后被异或(插入符号' ^'意味着XOR两个值)与1给出0(1 ^ 1 == 0 )。当x在LSB中为0且与1进行异或时,它会产生1(0 ^ 1 == 1)。

答案 2 :(得分:0)

仅当!(x&0x0000FFFF)的最后16位全部为0时,

x才会为真。 &是按位的,而0x0000FFFFF是以16 1&s结尾的数字。 因此,如果所有16个尾随位都为0,那么和的结果是0(因此FALSE和1反转真值)因为如果最后16个中至少有一个1,那么和相应的1在常量将为1.那么the和不是0(所以TRUE和!反转真值)。

所以代码说:如果最后16位是1,则将16加到n并抛出最后16位(这就是x >>= 16所做的)。
下一行以类似的方式说: 如果(可能缩短的x)的最后8位为0,则向n添加8并将最右边的8位丢弃,依此类推4位和2位

如果最右边的位(x&1)为0,则最后一行加1,否则为0(1^1 = 0)。

所以说如果最右边的15位为0,则第一个if将为假,n保持为0。 第二个是真的,因为我们有超过8个。新的x将有7个0位, 并且n = 8 第三个也是真的(我们仍然有4个或更多),所以新的x在移位后有3个0位,n = 12。 第四个也是真的(2个或更多0' s)所以新的x有1个0位且n = 14。
最后的语句加1,所以得n = 15。

因为我们使用2的递减幂,所以我们不需要循环。我们以这种方式得到所有可能的n值(除了32,对于输入x=0,一个完全正确的函数应该检查那个和早期中止。

答案 3 :(得分:0)

n += (x & 1) ^ 1; // anyway what does this do ?

这会检查最右边的位。无论是设置还是未设置。

如果设置了,那么没有另外0加到尾随零的运行总数上,所以n + = 0。

如果没有设置,那么还有另外一个0来添加到尾随零的运行总数,所以n + = 1.

另外,你的例子没有编译,它缺少两个;如下:

unsigned tzr(unsigned x)
{
    unsigned n; /* number of bits */

    n = 0;
    if (!(x & 0x0000FFFF)) { n += 16; x >>= 16; }
    if (!(x & 0x000000FF)) { n += 8; x >>= 8; }
    if (!(x & 0x0000000F)) { n += 4; x >>= 4 } // won't compile due to missing ;
    if (!(x & 0x00000003)) { n += 2; x >>= 2 } // won't compile due to missing ;

    n += (x & 1) ^ 1; // anyway what does this do ?

    return n;
}

此外,您始终可以尝试打印输出数据,例如,每个2的幂都有多个尾随零,但只有奇数量的尾随零从n += (x & 1) ^ 1;增加1 ...

cout << tzr(9) << endl << endl; // 1001 (not a power of two )
cout << tzr(8) << endl << endl; // 1000 (8>>2 & 1)^1==1
cout << tzr(4) << endl << endl; // 0100 (4>>2 & 1)^1==0
cout << tzr(2) << endl << endl; // 0010 (   2 & 1)^1==1
cout << tzr(1) << endl << endl; // 0001 (   1 & 1)^1==0

tzr(9)== 0 ==&gt; 0 +(9&amp; 1)^ 1 == 0 + 0

tzr(8)== 3 ==&gt; 2 +(8>&gt; 2&amp; 1)^ 1 == 2 + 1

tzr(4)== 2 ==&gt; 2 +(4>&gt; 2&amp; 1)^ 1 == 2 + 0

tzr(2)== 1 ==&gt; 0 +(2&amp; 1)^ 1 == 0 + 1

tzr(1)== 0 ==&gt; 0 +(1&amp; 1)^ 1 == 0 + 0

程序以退出代码结束:0

答案 4 :(得分:0)

你说,“我需要一个程序,它返回一个数字的二进制表示中的尾随零的数量。”但它必须是你找到的程序吗?这是一个替代解决方案,它只在一行代码中实现tzr(),

#include <stdio.h>
#include <stdlib.h>

int tzr(int n) { /* --- every time n is even, add 1 and check n/2 --- */
  return ( (n/2)*2 == n? 1+tzr(n/2) : 0 ); }

int main ( int argc, char *argv[] ) { /* --- test driver --- */
  int n = (argc>1? atoi(argv[1]) : 1000);
  printf("tzr(%d) = %d\n", n,tzr(n)); }

这更容易理解吗?

(P.S。你可以使用位掩码和移位而不是我的除法和乘法。这可能会更有效率,但我认为我的方式可能会更直接阅读。)