【三种方法】求一个整数存储在内存中二进制中的1的个数附两道课外练习题

简介: 【三种方法】求一个整数存储在内存中二进制中的1的个数附两道课外练习题

法一:取模与取余

分析:

       数据在内存中以补码形式存储

      题目要求我们求一个数在内存中二进制中1的个数,从这里可以想到,我们需要定义一个变量count来计数,再得到二进制的每一位,并且再判断它是否为1,这道题就差不多解决了。但问题就是如何得到二进制的每一位?我们知道二进制的每一位要么是0要么是1,因此求二进制中1的个数,只需%2,看得到的余数是不是1,如果是1,count++,之后/2使得这个二进制去掉最后一位,如此循环往复,直到该数字为0,count的值就是1的个数。

#include <stdio.h>
int main()
{
  int num = 10;//00....01001
  scanf("%d", &num);
  int count = 0; //计数
  while (num)
  {
    if (num % 2 == 1)
      count++;
    num = num / 2;
  }
  printf("二进制中1的个数 = %d\n", count);
  return 0;
}

运行结果:

从运行结果来看,-1的输出是错的,因此,这种实现方法肯定有问题。

那是为什么呢?

现在,我们来找找问题:

输入-1时,由于num为负数,所以num/2的值为向下取整的结果,即-1/2=-1,此时num的值仍为-1,因此while循环不会结束,导致程序出现死循环。同时,由于num的值一直为负数,所以num % 2 的结果始终为0,导致count的值一直为0。

因此这个问题是由于-1是负数,那我们把num类型定为无符号整型,会整型会怎样呢?

     修改后:

#include <stdio.h>
int main()
{
    unsigned int num = 10;
    scanf("%u", &num);
    int count = 0; //计数
    while (num)
    {
        if (num % 2 == 1)
            count++;
        num = num / 2;
    }
    printf("二进制中1的个数 = %d\n", count);
    return 0;
}

运行结果:

答案正确。

这是因为将输入的无符号整数 num 强制转换为有符号整数 int 类型,导致输入的负数被解释为一个很大的正整数。当输入-1时,它被当作非常大的正整数(4294967295)来处理,然后计算其补码二进制表示中 1 的个数,最终输出结果为 32。

法二:按位与和移位操作符

    这里先上代码,再分析

int main()
{
  int num = -1;
    scanf("%d", &num);
  int i = 0;
  int count = 0; //计数
  for (i = 0; i < 32; i++)
  {
    if (num & (1 << i))
      count++;
  }
  printf("⼆进制中1的个数 = %d\n", count);
  return 0;
}

   我们以-1来分析:

  • 数据在计算机中的存储形式是补码,而程序打印数据是以二进制的原码形式转换成十进制
  • -1的原码:10000000000000000000000000000001
    -1的反码:11111111111111111111111111111110
    -1的补码:11111111111111111111111111111111
  • (1 << i)   :1向左移动 i 位

当 i 为0时,结果为0000......01;

当 i 为1时,1向左移动一位,最左边丢掉一位,右边补一个0,结果为0000.....10;

当 i 为2时,1左移两位,丢掉最左边两位,在最右边补两个0,结果为0000.......100

因此(1<<i)产生的效果就是让数字1存储在内存中唯一的二进制位 1 移动 i 位

  • &按位与运算符:对应二进制位有0,则0
  • num & (1 << i):当 i 为0 时,1111....11& 0000....01结果为0000.....01,即1,为真,count++;

       当 i 为1时,1111......11&0000....10结果为0000....10,即2,为真,count++;

       .

       .

       .

       最终结果为32

  法三:利用算法去掉二进制中最右边的1

       一个数字与上这个数字减一的数,该数二进制最右边的1必然会消除掉,以此类推,从右往左,每一次进行按位与操作,都会取消掉一个1,直到该数字变为0,跳出循环,就得到了该数字二进制中1的个数。

以3(0000...0011)为例:

num:             0000...0011 —>       3

num - 1:          0000...0010 —>       2  

num = 3 & 2:   0000...0010  —>      2

num - 1:          0000...0001  —>      1

num = 2 & 1:   0000...0000 —>        0

跳出循环

#include <stdio.h>
int main()
{
  int num = -1;
    scanf("%d",&num);
  int i = 0;
  int count = 0; //计数
  while (num)
  {
    count++;
    num = num & (num - 1);
  }
  printf("⼆进制中1的个数 = %d\n", count);
  return 0;
}

       已经到这里了,来道课外练习巩固吧!

课外练习1:用位运算判断一个数是否是2的次方数

       先自己思考动动手,再来看把!

#include <stdio.h>
int isPowerOfTwo(int n) 
{
  if (n <= 0) 
  {
    return 0;
  }
  return (n & (n - 1)) == 0;
}
int main()
{
  int num;
  scanf("%d", &num);
  if (isPowerOfTwo(num))
  {
    printf("%d 是2的次方数。\n", num);
  }
  else 
  {
    printf("%d 不是2的次方数。\n", num);
  }
  return 0;
}

简单分析:一个数如果是2的次方数,则它的二进制表示中只有一位是1,例如:1、2、4、8、16等。

课外练习2:编写代码将13二进制序列的第5位修改为1,然后再改回0

13的2进制序列: 00000000000000000000000000001101

将第5位置为1后:00000000000000000000000000011101

将第5位再置为0:00000000000000000000000000001101

同样的,还是要先自己练习了,再来看!

二进制序列的第n位修改为1的公式:a = a | (1 << n - 1)

分析:以a = a | (1 << n - 1)为例

         设 a = 2         // 0000...0010

  • (1 << i)   :1向左移动 i 位

       当 n为1时,结果为0000......01;

       当 n 为2时,1向左移动一位,最左边丢掉一位,右边补一个0,结果为0000.....10;

       当 n 为3时,1左移两位,丢掉最左边两位,在最右边补两个0,结果为0000.......100

       因此(1<<n - 1)产生的效果就是让数字1存储在内存中唯一的二进制位 1 移动 n - 1 位

  • | 按位或运算符:对应二进制位有1,则1
  • a |(1 << n - 1):设n为3时,0000....010 | 0000....100结果为 0000....110,这样就把第i位改为1了

以此类推,二进制序列的第n位修改为0的公式:a = a & ~ (1 << n - 1)

代码:

#include <stdio.h>
int main()
{
  int a = 13;
  a = a | (1 << 4);
  printf("a = %d\n", a);
  a = a & ~(1 << 4);
  printf("a = %d\n", a);
  return 0;
}

运行结果:

只有一点小小归纳,希望能帮到大家!

如果大家发现知识点错误的话,请帮忙指出,十分感谢!!

也请大家帮忙点赞、评论,这将督促我前行,大家一起加油!!!


目录
相关文章
|
24天前
|
存储 监控 安全
内存卡数据恢复,3个方法帮你找回丢失的照片和视频
今天,针对内存卡数据恢复,本期做一个详细的归纳,分析常见的数据丢失原因、详细的数据恢复步骤、以及如何保护内存卡数据。
内存卡数据恢复,3个方法帮你找回丢失的照片和视频
|
20天前
|
存储 固态存储 芯片
计算机中内存与存储
【7月更文挑战第28天】
24 1
|
24天前
|
存储 弹性计算 程序员
新手程序员如何阿里云服务器配置?新人开发者CPU内存带宽存储怎么选?
对于新手开发者、个人或学生选择阿里云服务器,推荐ECS经济型e实例(ecs.e-c1m1.large),适用于小型网站或轻量应用。配置2核2G内存、3M固定带宽、40G ESSD系统盘,仅99元/年且续费同价。
|
25天前
|
监控 安全 Java
JVM内存问题之排查Direct Memory泄漏有哪些常用方法
JVM内存问题之排查Direct Memory泄漏有哪些常用方法
|
6天前
|
存储 编译器 C语言
数据在内存中的存储
数据在内存中的存储
|
6天前
|
存储 Java 图形学
UNITY性能优化☀️一、GC介绍与Unity内存管理方法
UNITY性能优化☀️一、GC介绍与Unity内存管理方法
|
1月前
|
运维 DataWorks 安全
DataWorks产品使用合集之如何查看空间资源、CPU、内存和存储空间容量
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
|
29天前
|
存储 Rust JavaScript
Rust 问题之TypeScript 代码,变量 s 存储在栈内存中还是堆内存中如何解决
Rust 问题之TypeScript 代码,变量 s 存储在栈内存中还是堆内存中如何解决
|
1月前
|
机器学习/深度学习 分布式计算 大数据
MaxCompute产品使用合集之如何查看空间资源、CPU和内存以及存储空间容量
MaxCompute作为一款全面的大数据处理平台,广泛应用于各类大数据分析、数据挖掘、BI及机器学习场景。掌握其核心功能、熟练操作流程、遵循最佳实践,可以帮助用户高效、安全地管理和利用海量数据。以下是一个关于MaxCompute产品使用的合集,涵盖了其核心功能、应用场景、操作流程以及最佳实践等内容。
|
1月前
|
存储 分布式计算 Hadoop
HadoopCPU、内存、存储限制
【7月更文挑战第13天】
100 14