二进制中1的个数问题 (超详细)

整数 二进制中1的个数

前两天遇到这个问题,第一反应就是%2和>>1这两个操作。
  大概思路:
  正数:%2,拿到原码(补码)的最右位,对于一个int类型的数进行32次判断,这是比较简单的理解。负数:虽然它的补码需要原码取反加一,但是原码和补码的共同点最右位是一致的,所以%2其实也是可以拿到补码的最右位。
  所以这个代码对于整数而言,都是行得通的。

    int num = -1;
	int count1 = 0;
	for (int i = 0; i < 32;i++) {
		if (num % 2 != 0) {
			count1++;
		}
		num = num >> 1;
	}

当然,这个思路差不多的方法还有

(((n >> i) & 1) == 1)

  按位与1,通过&1拿到最右位,>>31次,也可以计算出1的个数。

unsigned int n1 = -1;

  负数通过自动类型的提升赋给一个无符号的整数n1,我们对于正数n1 &2和 >> 操作也一样可以拿到1的个数  ( 原理:整数在计算机内存是以补码形式存在 )
这些方法整个思路呢都是相似的,针对int类型的整数,对其补码进行32次循环操作。

当然,我看到了一个最经典的方法n & (n-1)

    int n = -1;
	int count = 0;
	while (n) {
		n = n & (n - 1);
		count++;
	}
	printf("%d\n",count2);

刚开始看到这个n&n-1,真的是一脸懵逼,这都是哪跟哪,后面仔细的分析了下,的确也是这么一回事。给你们慢慢分析,逐字解析。

  还是从正数负数分开分析 (32位太长,我们用8位代替)

  正数:以红色框1为基准,(n-1)操作,补码从右往左开始,遇到1就停住,减去,也就是红色框1右边的0全部变成1,左边的原封不动,所以n&n-1也就是拿到了1左边的数,count++,n重新进入循环判断,如果不为0,就代表补码还存在1,重复操作。

都是补码的形式(用原码去理解,正数原码补码都一样)

  负数:我们要知道负数的补码是原码取反加一,对于一个(负数-1) & 负数,就很难去理解,所以我也是通过图片帮助理解。

  首先我们要知道 n-1在计算机底层的操作是n + (-1),我们通过一个数与32位1,也就是-1的补码形式,进行相加,溢出之后,就是n-1的效果,这里必须要再次感叹计算机的创造者。

  n-1:依然以红色框1为基准,与-1的补码进行相加,很清楚的可以看到,右边开始,第一个1,也就是红色框1变为0,红色框1左边的数都原封不动,为什么会这么神奇呢? 其实可以这样理解,n与-1相加,对于红框到最左边的1,都可以看成红框数1与-1所对应位置的1相加,产生的进1,与-1前面的所有1相加,溢出,本质上就是红框数1变0,左边部分不变,右边0变1。

  因此,n&(n-1也)是一样拿到了红色框1左边的原封不动的数,和正数也是同样的原理。
在这里插入图片描述(补码的形式,-1也用8位代替)

  总结一下,这个方法对于正负数都是一样的,以补码右边开始第一个1位基准,(n-1)保持该1的左边原封不动,右边取反,再&按位与运算,count++。新的n进行判断,n !=0 则代表里面还有1,n==0则代表n里面没有1,结束循环。
这个方法对于一个int类型的数,不需要32次循环执行,补码里面有几个1就执行几次循环!!!

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值