统计整数的二进制序列中1的个数 - C语言
我们知道,数据在内存中是以二进制的形式存储的,而整数的二进制的表示形式有三种:原码、反码、补码,在内存中整数以补码的形式存储。今天,我们就要编写C语言代码,统计一个整数在内存中的二进制序列中有多少个1。
在这里我们把这个功能封装成一个函数。而在函数具体实现之前,我们要创建一个函数环境,以检测函数运行结果。
函数环境
函数环境:
#include <stdio.h>
int number_of1(int );//函数声明
int main()
{
int num = 0;
scanf("%d", &num);
int ret = number_of1(num);//函数调用
printf("ret = %d\n", ret);//打印结果
return 0;
}
函数环境创建完成后,我们就要开始实现函数了。
函数number_of1
注:示例为了简便,二进制序列均只写了4位。
版本1 - 进制转换版本
进制转换版本:
要点:十进制转换成二进制的原理。
示例:
不完善版本
不完善版本:
int number_of1(int num)//版本1.0 - 进制转换 - 不完善版本
{
int count = 0;
while (num)//迭代
{
if (num % 2)
{
count++;
}
num = num / 2;
}
return count;
}
我们发现当num为非负整数时,函数实现的很成功,但当num为负数时,结果就会发生错误,原因是负数的原码和补码并不相同。
解决方法:可以将函数形参改为无符号整型,将负数在内存中存放的补码当成无符号的进行计算,就是将负数的补码当成原码,转换成对应的正数,再进行迭代。
完善版本
完善版本:
int number_of1(unsigned int num)//版本1.1 - 进制转换版本 - 完善版本
{
int count = 0;
while (num)
{
if (num % 2)
{
count++;
}
num = num / 2;
}
return count;
}
但是我们会发现,在声明函数时,函数的形参我们希望的是int类型,而进制转换的完善版本的函数形参与声明不同,所以我们要进一步思考还有什么方法可以实现该函数功能呢。
接下来就是我们的第二个版本,按位与1版本。
版本2 - 按位与1版本
按位与1版本:
示例:
int number_of1(int num)//版本2 - 按位与1版本
{
int count = 0;
while (num)//迭代
{
if (num & 1)
{
count++;
}
num = num >> 1;
}
return count;
}
但是我们很快发现,右移分为算术右移和逻辑右移两种,不同编译器可能不同,导致负数在算术右移的情况下左边补1,会陷入死循环,所以这个版本依然不能很好符合我们的要求,所以我们要继续思考其他版本。
注:此版本函数形参改为unsigned int可以实现函数功能,原理大致同进制转换中提到的差不多,这里不重新介绍,但修改后仍与函数声明时的函数形参不同。
版本3 - 按位与(num-1)版本
按位与(num-1)版本:
示例:
int number_of1(int num)//版本3 - 按位与(num-1)版本
{
int count = 0;
while (num)
{
count++;
num = (num & (num - 1));
}
return count;
}
经过运行调试,我们发现版本3很好的实现了统计有符号整数的二进制序列中1的个数。
版本3的运行结果:
num = 13
num = -1
经过一番努力,我们终于实现了统计整数二进制序列中1的个数的函数!