我需要一个程序,它返回数字的二进制表示中的尾随零的数量。我在网上找到了一个用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
的区别是什么?
答案 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。你可以使用位掩码和移位而不是我的除法和乘法。这可能会更有效率,但我认为我的方式可能会更直接阅读。)