求数字 15 以及数字-1的二进制表达式中有多少个一
目录
1.了解二进制表达
2.认识如何用代码计算数字的二进制中有几个一
了解二进制的知识
1.首先了解整形数字是如何储存的
拿整形数字 int a =15 (一个int 类型是4个字节,一个字节8个比特位 ,二进制中一个0或1占一个比特位所以一个整形a 需要用32个0.1来存储)
例如:15 的二进制可表示为00000000 00000000 00000000 00001111
2. 二进制转十进制的计算方法:从右往左数 第一个数字*2的0次方, 第二个数字*2的一次方,第三个数字*2的二次方,直至全部计算出来,再将其相加最后得到的值即为二进制转化成十进制的值)
例如:那上面的15为例 1*2的零次方+1*2的一次方+1*2的二次方+1*2的三次方=15
最左边的一位数字表示的是符号位(若数字位1则该数字为负数,若为0则为正数)
数据的储存使用的是二进制(正数储存的是源码,而负数储存的是补码(计算负数的二进制为有多少个1实际是计算其补码有多少个一)
15 源码 00000000 00000000 00000000 00001111
-1 源码 10000000 00000000 00000000 00000001
反码 11111111 11111111 11111111 11111110(反码是源码的符号位不变其他位按位取反)
补码 11111111 11111111 11111111 11111111 (补码是反码+1)(-1在存储中的样子)
计算某个数字用二进制表示其中1的个数
方法一: 用%2(取模符号)得到最右边数字 (若最右侧的一位为1的话则会模1;若为0则最右位为0)(每次取模后的得到的数为1是count++)
在将计算的数字/2(除以)得到该数字向右移一位的数字(舍弃最右边的一位)(此步是紧接这上一步操作,上一步判断最右位数字而这一步可以让第二位数字变成第一位,再进行判断改为数字)
详细看代码区
代码1:
只能用来计算正数
不能用来计算负数-1%2=0
-1/2=0
而-1的补码为11111111 11111111 11111111 11111111
第一位为1 与计算机结果冲突故不能用来算负数
#include <stdio.h>
int count_one(int b)
{
int count = 0;
while (b!=0)
{
if (b % 2 == 1) //b%2得到的是二进制最低为的值(即最右侧一位的值)
{
count++; //最右侧为1 ,count++得到数a的二进制表达式中1的个数
}
b /= 2; //b/2可看成二进制数列向右移了一位(则后续对其取模则是得到右侧第二位数字的值
}
return count;
}
int main()
{
int a = 7;
int ret = count_one(a);
printf("%d\n", ret);
return 0;
}
改进后的 代码2:该代码较代码中a的类型改为了无符号整型(即将-1的二进制表达是看成了一个有32个1的二进制位组合成的超大正数,最后计算-1的二进制中1个个数就可继续计算
int count_one(size_t b) //用size_t b 接收a
{
int count = 0;
while (b != 0)
{
if (b % 2 == 1)
{
count++;
}
b /= 2;
}
return count;
}
int main()
{
size_t a = -1; //代码进行完善,将原先a的int类型转化成size_t(unsigned int)即无
符号整形的类型,使得该函数可以计算负数的补码中1的个数
int ret = count_one(a);
printf("%d\n", ret);
return 0;
}
方法2:用右移>>符号和按位与&来计算1的个数
右移符号>>:将数字向右移1为(去掉了最右位,最左侧补0)
例如 a=15 00000000 00000000 00000000 00001111
b=a>>1 00000000 00000000 00000000 00000111 (则b=7)
数字与1按位与 (结果为1说明原数字最右位为1,为2则说明原数字为0)
例如 a=10 00000000 00000000 00000000 00001010
b=3 00000000 00000000 00000000 000000011
c=1 00000000 00000000 00000000 000000001
d=a&c 00000000 00000000 00000000 00000000 结果为0 ,a最右侧数字为0
e=b&c 0000000 00000000 000000000 00000001 结果为1,则b最右位为1
思路:当a&1 为1是count++,右移31次(使得最后计算的最右侧数字是原先最左侧的数字为止)和按位与则可统计到该数中所有1的个数
int count_one(int a)
{
int count = 0;
int i = 0;
for (i = 0;i < 32;i++)
{
if ((a >> i)&1 == 1)
{
count++;
}
}
return count;
}
int main()
{
int a = -1;
int ret = count_one(a);
printf("ret = %d\n", ret);
return 0;
}
方法3:利用a与a-1 和按位与的方法
按位与&符号的说明 : 两个数字的二进制中对应位都为1是两数按位与得到的数值的该位也为1,若同为0或一个1一个0 则得到的数字的该位为0
例如:a=000001
b=000101
则c=a&b= 000001
a与a-1按位与的思路和意义
a&(a-1) (为了简洁,一下只写二进制的后4位)拿a=10为例
a 1010
a-1 1001 a&=(a-1) 则a在每次计算都发生改变
a 1000 按位与一次中的1个1少掉了
a-1 0111
a 0000
思路:按位与第二次的到的数也比之前少了个1(找计算规律)
则可之与a-1按位与一次后a会少一个一
则可以统计按位与的次数来计算a 数中1的个数,当a 等于0时跳出计算
即可得到该数中1的个数
int count_one(int a)
{
int count = 0;
while (a) //巧妙之处当a=0时,while循环跳出
{
count++;
a &= (a - 1);
}
return count;
}
int main()
{
int a = 15;
int ret = count_one(a);
printf("ret = %d\n", ret);
return 0;
}
一