输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
首先要理解这个题考点不是要你怎么去表示原码、补码、反码之类的。在计算机中正数的表示就是他的原码,负数的表示就是他的补码。至于这几个码到底是怎么样的,后面再讲。
举例子,6的二进制表示为0000 0110(一般都是用8位表示原码)。6-1,二进制的表示为:0000 0110 - 0000 0001,这个时候,遇到0000 0110从右到左,第一个不为0的位,将其变为0,而这个位右边的全部变为1,也就是0000 0101。然后将0000 0101 与6 的原码进行“与”运算,即 0000 0101 & 0000 0110,得到0000 0100,这样进行一次之后就将6的原码的最右边的1变为了0。按照这个思想,我们一直进行此过程,直到n等于0。
综上分析,代码为:
public class Solution {
public int NumberOf1(int n) {
if(n == 0){
return 0;
}
int countOfOne = 0;
while(n != 0){
n = n & (n - 1);
countOfOne ++ ;
}
return countOfOne;
}
}
所谓原码就是机器数,是加了一位符号位的二进制数,正数符号位为0,负数符号位为1,计算机中存储、处理、运算的数据通常是8位、16位、32位或64位的。
这里以最简单的8位为例讲解。注意符号位是包含在8位中的其中1位,故可直观读出的数只有7位(只有后7位数可以按权展开)。有心人可能注意到原码是有缺陷的,它只能表示255种状态,因为00000000(+0)和10000000(-0)其实是一个数,因此原码的表示范围成了-127到+127,这个问题需要神奇的补码来解决,因为在补码中10000000被用来表示-128。
所谓反码,英语里又叫ones’ complement(对1求补),这里的1,本质上是一个有限位计数系统里所能表示出的最大值,在8位二进制里就是11111111,在1位十进制里就是9,在3位十六进制里就是FFF(再大就要进位了)。求反又被称为对一求补,用最大数减去一个数就能得到它的反,很容易看出在二进制里11111111减去任何数结果都是把这个数按位取反,0变1,1变零,所以才称之为反码。用原码求反码的方法是,正数不变,负数保留符号位1不变,剩下位按位取反。
所谓补码,英语里又叫two’s complement(对2求补),这个2指的是计数系统的容量(模),就是计数系统所能表示的状态数。对1位二进制数来说只有0和1两种状态,所以模是10也就是十进制的2,对7位二进制数来说就是10000000,这个模是不可能取到的,因为位数多一位。用模减去一个数(无符号部分)就能得到这个数的补,比如10000000-1010010=0101110,事实上因为10000000=1111111+1,稍加改变就成了(1111111-1010010)+1,所以又可以表述为先求反再加1。总结求补码的方法就是正数依旧不变,负数保留符号位不变,先求反码再加上1。记住了怎么求补码,接下来讲讲运算。通过原码的符号位和数值,我们能迅速指出它代表的数,判断其正负并进行四则运算,相比而言反码和补码对于人则显得过于晦涩。如果说原码是给人看的数字语言,那么补码就是计算机的数字语言。计算机不需要知道什么是正负、大小,这些判断对它而言过于复杂。事实上它存储、处理、传输的数都只有补码一种形式,人所做的加减乘除,在计算机里只通过相加和移位就能解决,这都来自于补码系统的内在自洽和巧夺天工的神奇魔力,也是后文要阐述的重点。
这里我是引用了引用了知乎网友的回答,更为详细的请参考这篇帖子:
知乎:原码、反码、补码