前言:一个数要存储在计算机中,是以该数二进制补码形式存储的。
法1(不推荐使用):
思路:当我们想要得到一个十进制数的每一位时,通常是采用模10除10的方法,模10得到最低位,除10得到高位,一直重复。那么想要得到一个数的二进制的每一位时,也可以采用模2除2的方法。
//求一个数存储在二进制数中1的个数(补码形式存储,所以算的是该数二进制补码中1的个数)
int count_one1(int m) //不适用于负数
{
int count = 0;
while (m)
{
if (m % 2 == 1) //最低位是1
{
count++;
}
m /= 2; //处理完最低位后将其丢掉
}
return count;
}
int main()
{
int n = 1;
int count = count_one1(n);
printf("%d
", count);
system("pause");
return 0;
}
但我们发现,一个负数不论%2还是/2结果永远是负数,所以用上述代码求解1的个数时永远都是0个,所以此种方法只适用于正数。
法二(推荐使用):
思路:我们知道一个数的二进制&1可得该数二进制最低位是0还是1,那么在此基础上,只要我们每次判断完最低位后右移一位就可以得到此时最低位的值,移31位便可得到该数二进制中1的个数。
int count_one2(int m) //必须要循环32次,效率太低
{
int i = 0;
int count = 0;
for (i = 0; i < 32; i++)
{
if (((m >> i) & 1) == 1)
{
count++;
}
}
return count;
}
int main()
{
int n = -1;
int count = count_one2(n);
printf("%d
", count);
system("pause");
return 0;
}
但是此种方法效率太低,如果一个数的二进制补码中1全部集中在低位,而高位无0,循环32次无疑拉低了程序的运行效率。
法3(推荐使用):
思路:我们发现一个数和该数减1后做按位与操作再赋给该数,即某数=某数&(某数-1),每执行一次,该数二进制最右边的1就会丢掉。那么我们便可以知道,只要一个数在变成0之前能执行多少次某数=某数&(某数-1)操作,该数的二进制便有多少个1。
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
int count_one3(int m)
{
int count = 0;
while (m)
{
m = m&(m - 1);
count++;
}
return count;
}
int main()
{
int n = -1;
int count = count_one3(n);
printf("%d
", count);
system("pause");
return 0;
}
此方法既适用于负数,又大大提高了程序执行的效率。