剑指offer 二进制中1的个数

题目描述:
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

一道好题!!
做完这道题,加深了我对计算机存储数字的形式以及位运算的理解!!

假设一个int为8字节

在计算机中,一个数字以二进制的形式存储,对于一个int类型的数字,最高位为符号位,0为正,1为负。他们全都是以补码的形式存储的。

对于一个正整数,它的原码反码补码都是它的二进制表示,而对于一个负数,它的源码是其相反数的二进制表示,然后最高位变为1,比如-1,按照上述说明,它的原码就是10000001,反码就是除了符号位,其余位置按位取反,即11111110,补码就是在反码的基础上+1,即11111111。

在这里有一个地方需要注意,那就是如果一个数的反码为11111111,那么它的补码怎么表示?其实反码11111111的原码是10000000,即-0,我们知道在0的前面加符号是没有意义的,因此,我们规定10000000表示-128,即负数多了一个表示,这也就是为什么我们通常所说的32位int的取值范围是[-2147483648 ~ 2147483647],而由于整数在计算机内是以补码的形式保存的,因此,-128的补码就规定为-10000000,它没有原码和反码,这样做也是的0有了唯一的表示。

我们回到这个题,他让我们求一个整数的二进制有多少个1,根据上述所说的,我们知道了整数以补码形式保存,因此,我们就不用纠结如何现将负数的补码求出来,再判断有几个1了,直接撸就行。。。

怎么撸又是一个问题,如果我们按照通常的做法,与1进行与运算,再右移这个数,但是注意负数可不能右移运算。在说原因之前,我们先明确两个概念:

逻辑右移:二进制右移后,用0补位。
算数右移:右移后,符号不变。

逻辑右移比较好理解,这里重点说一下算数右移:

比如-128,它的补码为10000000,算数右移就是首先右移一位,得到01000000,然后首位补1,得到最终的11000000,它的反码是10111111,原码是11000000,即-64。

明白了什么是算术右移和逻辑右移,我们也就明白了为什么负数不能右移运算,这是因为一般来说计算机右移操作都是算数右移,因此负数最高位1会被永久保留,这样得出的值必然是不正确的。

但是左移操作一定是逻辑左移,这样,对于任意一个数,先让其跟1与运算,然后将1左移,当1左移超出int位数限制的时候,我们就可以认为它检查完所有的位数了,这样就能得到正确的结果。

#include <cstdio>
#include <iostream>
using namespace std;

int main()
{
    int T;
    int n, ans;
    int f;
    scanf("%d", &T);
    while(T--){
        scanf("%d", &n);
        ans = 0;
        f = 1;
        while(f){
            if(f & n)
                ans++;
            f = f << 1;
        }
        printf("%d\n", ans);
    }
    return 0;
}

代码虽短,但是蕴含了很多知识,希望大家能够掌握,不足之处望指正。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值