Hello,大家好,我是一代,今天给大家讲解有关二进制的习题
习题一:求一个整数存储在内存中的二进制1的个数
提示:整数在内存存储时用的是补码
方法一:
很多人想到的方法就是对数进行/2和%2运算,代码如下:
#include<stdio.h>
int main()
{
int a = 0;
scanf("%d", &a);
int count = 0;
while (a)
{
if (a % 2 == 1)
{
count++;
}
a /= 2;
}
printf("%d", count);
return 0;
}
但仔细一想这样的代码是否有问题,当a=-1是否有问题
可以看到当输入 -1时输出是0,而正确的答案是32
-1的原码:10000000 00000000 00000000 00000001
-1的反码:111111111 111111111 111111111 111111110
-1的补码:111111111 111111111 111111111 111111111
可见-1存储在内存中二进制1的个数为32
但其实这样的代码还有可救之处,只要将其改为将a无符号整数就行
代码如下:
#include<stdio.h>
int main()
{
unsigned a = 0;
scanf("%d", &a);
int count = 0;
while (a)
{
if (a % 2 == 1)
{
count++;
}
a /= 2;
}
printf("%d", count);
return 0;
}
当输入-1时输出是32
方法二:
前面我们学习了移位操作符和按位与,我们就可以想到将1进行进行左移,让其与输入整数进行按位与&,以为1<<i除了所在位为1,其他位为0,进行&就可以保留输入的数二进制所在位为1的位。
代码如下:
#include<stdio.h>
int main()
{
int a = 0;
scanf("%d", &a);
int i = 0,count=0;
for (i = 0; i < 32; i++)
{
if (a & (1 << i))
{
count++;
}
}
printf("%d", count);
return 0;
}
但代码其实还有优化的空间
方法三:
这里使用这种方法之前,我跟大家先将解一下这种算法。
当a=15时
a的补码:1111
a-1的补码:1110
a=a&(a-1)补码为1110
a-1的补码为1101
a=a&(a-1)的补码为1100
a-1的补码为1011
a=a&(a-1)的补码为1000
a-1的补码为0111
a=a&(a-1)的补码为0000
可以观察到每次a=a&(a-1)后a的二进制中1的个数减少1个,直到a为0是结束。
于是就可以写如下代码:
#include<stdio.h>
int main()
{
int a = 0,count=0;
scanf("%d", &a);
while (a)
{
a = a & (a - 1);
count++;
}
printf("%d", count);
return 0;
}
习题二:将13二进制的第5位修改为1,再修改为0
修改特定位置的二进制位,就可以将1左移i位,再与数进行就行按位与和按位或将特定位修改位1或者0
代码如下:
#include<stdio.h>
int main()
{
int a = 13;
a = a | (1 << 4);
printf("%d\n", a);
a = a & ~(1 << 4);
printf("%d", a);
return 0;
}
这里有个符号~是按位取反,即把所有二进制位1变成0,0变成1。
习题三:判断一个数是否是2的n次方
上面我们讲了一种算法n&(n-1),一个数是2的n次方,所对应二进制位就只有一个1。
于是就有如下代码:
#include<stdio.h>
int main()
{
int a = 0;
scanf("%d", &a);
if ((a & (a - 1))==0)
{
printf("%d是2的次方数",a);
}
else
{
printf("%d不是2的次方数", a);
}
return 0;
}
注意:位运算符的优先级低于关系操作符,所以a&(a-1)要加上括号