算法通关村第十一关青铜挑战——移位运算详解

大家好,我是怒码少年小码。

计算机到底是怎么处理数字的?

数字在计算机中的表示

机器数

一个数在计算机中的二进制表示形式,叫做这个数的机器数。

机器数是带符号的,在计算机用一个数的最高位存放符号,正数为0,负数为1。比如,十进制中的数+3,计算机字长为8位,转换成二进制就是00000011。如果是-3,就是10000011。这里的 00000011 和 10000011 都是机器数。

真值

将带符号位的机器数对应的真正数值称为机器数的真值。

例如上述-3的机器码10000011的真值是-3而不是131。(10000011换成十进制是131)


计算机对机器数的表示进一步细化:原码, 反码, 补码。

原码

第一位表示符号,其余位表示值。例如如果是八位二进制:

  • [+1] 原 = 0000 0001
  • [-1 ] 原 = 1000 0001

八位二进制有一位用于表示符号,所以原码的表示范围是[-127,127]。

反码

  • 正数的反码是其本身
  • 的反码是在其原码的基础上,符号位不变,其余各位取反。
  • [+1] = [00000001]原 = [00000001]反
  • [-1] = [10000001]原 = [11111110]反

补码

  • 正数的补码就是其本身
  • 负数的补码是在其原码的基础上你,符号位不变,其余各位取反,最后+1(也就是在反码的基础上+1)
  • [+1] = [00000001]原 = [00000001]反 = [00000001]补
  • [-1] = [10000001]原 = [11111110]反 = [11111111]补

看到这里你应该知道一个数在计算机中有三种编码方式。

拓展:反码、补码在人脑无法直观的看出来它的数值,那为啥还要有它们?

答:人脑可以知道第一位是符号位,在计算的时候我们会根据符号位选择对真值区域的加减。但是计算机要辨别"符号位"就必须获得全部的位的数据才可以,显然会让计算机的基础电路设计变得十分复杂! 于是人们想出了将符号位也参与运算的方法。例如:根据运算法则减去一个正数等于加上一个负数。

1+(-1)=[00000001]反 + [11111110]反 = [11111111]反=[10000000]原 = -0

"0"的表示有点奇怪,+0和-0是一样的,而且0带符号是没有任何意义,而且要浪费[0000 0000]原和[10000000]原两个编码来表示0。于是补码的出现,解决了0的符号以及两个编码的问题:

1 + (-1) =[0000 0001]原 + [10000001]原= [00000001]补 + [1111 1111]补= [0000 0000]补=[0000 0000]原

[10000000]表示-128,所以补码的范围是[-128, 127]。(八进制中)

位运算规则

位运算主要有:与、或、异或、取反、左移和右移,其中左移和右移统称位移位运算,移位运算又分为算术移位和逻辑移位。

与运算的符号是&

运算规则:对于每个二进制位,当两个数对应的位都为1时,结果才为1,否则结果为0。

0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1

或运算的符号是|

运算规则:对于每个二进制位,当两个数对应的位都为0时,结果才为0,否则结果为1

0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1

异或

异或运算的符号是⊕(代码表示是^)

运算规则:当两个数对应的位相同时,结果为0,否则结果为1

00 = 0
01 = 1
10 = 1
11 = 0 

取反

取反运算的符号是~

运算规则:对一个数的每个二进制位进行取反操作,0变成1,1变成0

~0 = 1
~1 = 0

移位运算

移位运算按照移位方法分类可以分成左移和右移,按照是否带符号分类可以分成算术移位和逻辑移位

  • 原始: 0000 0110 ===> 6
  • 右移一次: 0000 0011 ===> 3 相当于除以2
  • 左移一次: 0000 1100 ===> 12 相当于乘以2

左移运算的符号是<< ,左移运算时,将全部二进制位向左移动若干位,高位丢弃,低位补0。对于左移运算,算术移位和逻辑移位是相同的。
右移运算的符号是>>。右移运算时,将全部二进制位向右移动若干位,低位丢弃,高位的补位由算术移位或逻辑移位决定:

  • 算术右移时,高位补最高位;
  • 逻辑右移时,高位补0
  • 示例1:29的二进制表示是0001 1101。 29左移2位的结果是116,对应的二进制是01110100;29左移3位的结果是-24,对应的二进制是11101000
  • 示例2:-50的二进制表示是11001110(补码)。-50算术右移2位的结果是-13,对应的二进制表示是11110011;-50逻辑右移2位的结果是51,对应的二进制表示是00110011

拓展:计算机内部的右移运算采取的是哪一种?

答:

  • 对于 C/C++ 而言,对于有符号类型,右移运算为算术右移;对于无符 号类型,右移运算为逻辑右移。
  • 对于 Java 而言,不存在无符号类型,所有的表示整数的类型都是有符号类型,因此需要区分算术右移和逻辑右移。在Java中,算术右移的符号是>>,逻辑右移的符号是 >>>。

位运算常用技巧

位运算代码套路

获取

将 1 左移 i 位,得到形如 00010000 的值,接着堆这个值与num执行”位与“操作,从而将i位之外的所有位清零,最后检查该结果是否为零。不为零说明i位为1,否则i位为0。代码如下:

boolean getBit(int num,int i){
	return ((num&(1<<i))!=0);
}

设置

setBit先将1左移i位,得到形如00010000的值,接着堆这个值和num执行”位或“操作,这样只会改变i 位的数据。这样除i位外的位均为零,故不会影响num的其余位。代码如下:

int setBit(int num,int i){
	return num | (1 << i);
}

清零

与setBit相反,将1左移i位获得形如00010000的值,对这个值取反进而得到类似11101111的 值,接着对该值和num执行”位与“,故而不会影响到num的其余位,只会清零i位。

int clearBit(int num,int i){
  int mask = ~(1 << i);
  return num &mask;
}

更新

这个方法是将setBit和clearBit合二为一,首先用诸如11101111的值将num的第i位清零。接着将待写入 值v左移i位,得到一个i位为v但其余位都为0的数。最后对之前的结果执行”位或“操作,v为1这num的i 位更新为1,否则为0:

int updateBit(int num,int i,int v){
 int mask=~(1<<i);
 return (num&mask)|(v<<i);
}

END

说实话,更文到这里其实有点累了,学计算机也有点累了。网上负面消息一大堆,近几年就业形势不好,自己在学的东西也得不到正向反馈,有时候真的觉得自己挺废物的😔。或许看到这的小伙伴也和我有一样的想法,这里我想分享一段翁凯老师的话:

计算机的所有东西都是人做出来的,别人能想得出来的,你也一定能想得出来。在计算机里头没有任何黑魔法,所有的东西只不过是我现在不知道而已,总有一天我会把所有的细节、所有的内部的东西都搞明白了。

这贼船都上了,咱就好好走下去吧😁

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值