(二进制)操作符详解

二进制与进制转换

2进制转10进制

无论对应什么进制而言,数值都是每一位的值乘以对应权重之和

10进制转2进制 

方法1:不断模2除2(取模和整除),直到为0,最后在把余数由下往上组合即为二进制

方法2:当你对2进制熟悉后,可以根据二进制的值进行大致的估算 

比如,上图中,求21的二进制,则先估算1111对应15,所以加一位,从10000开始 ,然后看下一位权重为8,加上就超过21了,就再看下一位为4……依此类推,最终算出10101

2进制转8进制和16进制 

8进制

从2进制序列中右边低位开始向左 每3个2进制位 会换算⼀个8进制位, 剩余不够3个 2进制位的 直接换算

16进制 

从2进制序列中右边低位开始向左 每4个2进制位 会换算⼀个16进制位, 剩余不够4个2 进制位的 直接换算

原码,反码,补码

整数的2进制表示方法有三种,即 原码、反码和补码
有符号 整数的三种表示方法均有符号位和数值位两部分,2进制序列中, 最高位 的1位是被当做 符号位 ,剩余的都是数值位。
符号位都是用 0表示“正” ,用 1表示“负”

  

这里有两种方法把补码变回原码 ,第一种是减1,除符号位按位取反,第二种是直接除符号位按位取反,再加1

对于整形来说:数据存放内存中其实存放的是补码。

 因为补码与原码相互转换,都可以除符号位按位取反,加1获得,所以运算过程相同

位移操作符

左移操作符 

左移抛弃(包括符号位),右边补零;左移不改变m本身,只是会输出一个结果到n

上述例子中,-3的补码是11111111  11111111 11111111 11111101 ,那么我左移30位,最高位就为0,就变成超级大的正数

右移操作符 

右移运算分两种(大部分编译器上是算术右移)
1. 逻辑右移:左边⽤0填充,右边丢弃
2. 算术右移: 左边 ⽤原该值的 符号位填充 ,右边丢弃

对于未溢出整数来说,左移一位,相当于乘以2,右移一位,相当于除以2 

位操作符

&按位与,有0则为0,全1才为1 

| 按位或,有1则为1,全0才为0

^按位异或,相同为0,相异为1

你可以动手算算下面三个位运算哦~ 

 

 我们再来看看^按位异或的神奇性质,下列输出的a结果为多少?

相信你已经发现了奥秘,在异或的过程中其余数都有伴,而只有6是单身狗,嘻嘻~

所以,两个相同的数异或会消失,只有单独的会留下 

我们再来看一道面试题:不能创建临时变量(第三个变量),实现两个数的交换 

可能有很多小伙伴是这样实现的,乍一看好像没什么问题,实际上如果a和b是两个非常大的整数,它们之间的运算可能会造成溢出,导致结果出错 

实际上,这里利用刚刚异或的性质就能轻松解决 ,这里a先存下a^b,等到与b异或时,里面的b相同就消失了,只剩下a,就可以赋给b;同样,实际上为a的b与实际上为a^b的a异或,a相同就消失了,留下b赋给a

这就好像一个破译密码的过程,a^b是加密过的密文,用a密码本就可以破译出b的信息,用b密码本就可以破译出a的信息 

练习 

 练习1:求一个整数存储在内存中的二进制中1的个数

方法一 

根据定义,模2除2,看看有多少1

这样看,好像可以,其实这段代码还有漏洞 

当输入的数为负数时,就失效了 。那要怎么改呢?其实很简单,把输入类型改为无符号整型即可,这样编译器就会把负数看为一个特别大的正数了。

方法二 

运用位移操作符与位操作符,让每一位与1按位与,1&1为1,0&1为0,这样就可以算出二进制位中有多少个1 

 

方法三 

这个方法也是最高效的,利用每运算一次n&(n-1),就会消去二进制中最右边的1的性质

我们可以发现,下图中n每进行一次这种运算,二进制位最右边的1就变成0,直到全为0 

这种方式是不是很好?达到了优化的效果,但是难以想到。 

这个方法还可以有很多迁移运用,比如这道题:判断一个数n是否是2的次方数

练习2: 二进制位置0或者置1

编写代码将13⼆进制序列的第5位修改为1,然后再改回0 

置为1 

置为1:将1左移4位,再与13按位或,1和0为1,0和0为0,只把第5位改成1 

置为0 

置为0:将1左移4位,再按位取反,得到11111111 11111111 11111111 11101111,再去和13按位与,1和0为0,1和1为1,只把第5位改成0

练习3:求两个数二进制中不同位的个数

这题其实就是前面练习1的变式,分为两步。第一步,先把两个数异或,根据异或的特点,相同为0,相异为1,这样只要统计1的个数,就是二进制位中不同位的个数。第二步,利用n&(n-1)高效消除1的特点,来统计1的个数 

代码如下: 

int main()
{
    int a, b;
    while (scanf("%d %d", &a, &b) != EOF)
    {
        int c = a ^ b;
        int count = 0;
        while (c)
        {
            c = c & (c-1);
            count++;
        }
        printf("%d\n",count);
    }
    return 0;
}

  • 8
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值