如何使用C判断数字的二进制表示中是否有1?

已经存在关于计算一个数中有多少1 s的问题,但这个问题是关于判断是否存在偶数或奇数的1。

不允许任何循环或条件(包括switch)语句。 此外,应避免使用除法,乘法或模数运算符。 更具体地说,我们可以假设它是一个32位无符号整数。

实际上我已经有了一个实现,但我无法弄清楚它的工作原因。 任何certificate其正确性或任何新想法都会非常有帮助。

 int even_ones(unsigned x) { x ^= x>>16; x ^= x>>8; x ^= x>>4; x ^= x>>2; x ^= x>>1; return !(x & 1); } 

我假设您知道exclusive或^操作的作用 – 如果两个位设置为相同的值,则结果为0 – 否则为1 。 因此,当我有两个一位数字A和B时,如果A和B都是0 ,或者两者都是1 ,则A^B将为零。 换句话说 – AB的总和是偶数……

现在让我们一次做两位:C和D是两位数。 以下是可能的组合:

 CDC^D 00 00 00 even 01 00 01 odd 10 00 10 odd 11 00 11 even 00 01 01 odd 01 01 00 even 10 01 11 even 11 01 10 odd 00 10 10 odd 01 10 11 even 10 10 00 even 11 10 01 odd 00 11 11 even 01 11 10 odd 10 11 01 odd 11 11 00 even 

正如您所看到的 – 每个实例中的操作将位数减少一半,如果您以奇数开始,则得到的1位数是奇数(因为1对的取消,但所有其他都是不变)。

现在应该明白为什么当你从更大的数字(4位,8位,16位)开始时,同样的事情仍然是真的。 实质上,您的算法以32位数字开头,并将其分成两个16位数字。 通过摆脱“双1”,它将位数减少一半; 然后对剩下的一半进行操作,并重复直到只剩下一个位。 通过测试该位是一(奇数)还是零(偶数),您可以得到答案。

如果不清楚,像x ^= x>>16这样的操作实际上将前16位移到底部16并在那里产生异或。 它实际上并没有清除顶部位,所以“留下了一团糟”。 但算法忽略了后面的混乱。 请参阅以下内容(为简单起见,以8位开头):

 x = abcdefgh x >> 4 = 0000abcd new x = abcdijkl x >> 2 = 00abcdij new x = abmnopqr x >> 1 = 0abmnopq new x = astuvwxy 

在这里,最后一个数字yrq的XOR,它又是l,jk,i的XOR k,i ; 这些又分别是h,df,bg,ce,a的XOR。 如你所见,你最终获得了所有位的XOR; 正如我在上面解释的那样,这意味着“全部偶数”或“全部奇数”,这取决于最低有效位现在是1还是0

我希望这有帮助。

以下是快速计算字节或字的奇偶校验的几种变体 。 究竟哪种方法最快取决于您的CPU以及不同基本操作相对于彼此的速度。 因此,如果这在某种程度上是您的应用程序的瓶颈,您应该分析每个应用程序以找出哪个在您的目标计算机上运行最佳。

您的解决方案与“并行计算奇偶校验”实现非常相似,只是在最后没有聪明的优化。 这里发生的是,在每一步中,你将一半的位与另一半的位进行异或,直到你只剩下一位。 如果存在偶数1,则字的奇偶校验为0;如果存在奇数1,则字的奇偶校验为1; 或者等效地,字的奇偶校验只是字中所有位的XOR。

由于XOR运算符是可交换的和关联的,我们可以重新排序单词中的所有位如何被异或。 因此,不是通过将每个位与结果进行异或运算来计算所有位的XOR,而是使用较低的一半位对较高的一半进行异或,将我们关注的位数减少一半; 当剩下一点时,我们就完成了。

注意,此XOR是最重要的16位,最低有效16位:

 x ^= x>>16; 

然后该系列继续对具有最低有效4位的第二个最低有效8位进行异或,注意最重要的16位现在只是垃圾,无论发生什么都可以忽略:

 x ^= x>>8; 

依此类推,我们继续将具有最低有效4位的第二个最低有效4位进行异或,直到达到1位为止; 到目前为止,除了最低有效位之外的每个位都是垃圾,最后一行只是按位使用,而1用来获得最低有效位并翻转它以进行均匀性测试。

也许,如果你这样写它会更容易理解:

 int even_ones(unsigned x) { a = (x ^ x>>16) & 0x0000FFFF; b = (a ^ a>>8) & 0x000000FF; c = (b ^ b>>4) & 0x0000000F; d = (c ^ c>>2) & 0x00000003; e = (d ^ d>>1) & 0x00000001; return !(e&1); } 

为什么这样做? 因为XOR相当于没有进位的按位加法。

希望这可以帮助 ::

  enum { EVEN, ODD } even_odd; unsigned int check_even_odd_no_of_ones(unsigned int num) { if(num_of_ones(num) & 1) return ODD; else return EVEN; } 

谢谢