基础
程序中的数在计算机内存中都是以二进制的形式存在的,位运算就是直接对整数在内存中对应的二进制位进行操作。
二进制
十进制转二进制时,采用“除 2 取余,逆序排列”法
- 用 2 整除十进制数,得到商和余数;
- 再用 2 整除商,得到新的商和余数;
- 重复第 1 和第 2 步,直到商为 0;
- 将先得到的余数作为二进制数的高位,后得到的余数作为二进制数的低位,依次排序;
1 | 101 % 2 = 50 余 1 |
逆序排列即二进制中的从高位到低位排序,得到 7
位二进制数为 1100101
,如果要转换为 8
位二进制数,就需要在最高位补 0
。即十进制数的 8
位二进制数为 01100101
。
预备知识
根据冯~诺依曼提出的经典计算机体系结构框架。一台计算机由运算器,控制器,存储器,输入和输出设备组成。其中运算器,只有加法运算器,没有减法运算器(据说一开始是有的,后来由于减法器硬件开销太大,被废了 )
所以,计算机中的没法直接做减法的,它的减法是通过加法来实现的。
你也许会说,现实世界中所有的减法也可以当成加法的,减去一个数,可以看作加上这个数的相反数。当然没错,但是前提是要先有负数的概念。这就为什么不得不引入一个该死的符号位。
原码,反码,补码的产生过程,就是为了解决,计算机做减法和引入符号位(正号和负号)的问题。
原码
最简单的机器数表示法。用最高位表示符号位,‘1’表示负号,‘0’表示正号。其他位存放该数的二进制的绝对值。
反码
原码最大的问题就在于一个数加上他的相反数不等于零
例如:0001+1001=1010 (1+(-1)=-2)
0010+1010=1100 (2+(-2)=-4)
于是反码的设计思想就是冲着解决这一点,既然一个负数是一个正数的相反数,那我们干脆用一个正数按位取反来表示负数试试
反码:正数的反码还是等于原码
负数的反码就是他的原码除符号位外,按位取反。
用反码解决原码问题
1 | 0001+1110=1111 (1+(-1)= - 0) |
做一下两个负数相加
1 | 1110(-1)+1101(-2)=1011(-4) |
它解决了相反数相加的问题,但是让负数相加出错了
补码
所有数据的运算都是使用补码
补码:正数的补码等于他的原码
负数的补码等于反码+1。
(这只是一种算补码的方式,多数书对于补码就是这句话)
并不是反码+1就定义为补码。只不过是补码正好就等于反码加1罢了
原码反码补码的相互转换
首先,正数的原码,反码,补码都是相同的。
所以,这里讨论负数的原码,反码,补码的相互转化问题。
- 负数原码和反码的相互转化
负数原码转化为反码:符号位不变,数值位按位取反。
如:
1 | 原码 1100 0010 |
负数反码转化为原码:符号位不变,数值位按位取反。
1 | 反码 1011 1101 |
- 负数原码和补码的相互转化
负数原码转化为补码:符号位不变,数值位按位取反,末尾加一。
1 | 原码 1100 0010 |
负数补码转化为原码:符号位不变,数值位按位取反,末尾加1。
1 | 补码 1011 1110 |
- 负数反码和补码的相互转化
负数反码转化为补码:末尾加1。
1 | 反码 1011 1101 |
负数补码转化为反码:末尾减1(注意,此处的反码是指原码的反码)。
1 | 补码 1011 1110 |
运算符
概览
按位与运算 &
定义:参加运算的两个数据,按二进制位进行“与”运算。
1 | 0&0=0 0&1=0 1&0=0 1&1=1 |
总结:两位同时为1,结果才为1,否则结果为0。
按位或运算 |
定义:参加运算的两个对象,按二进制位进行“或”运算。
1 | 0|0=0 0|1=1 1|0=1 1|1=1 |
总结:参加运算的两个对象只要有一个为1,其值为1。
异或运算符 ^
定义:参加运算的两个数据,按二进制位进行“异或”运算。
1 | 0^0=0 0^1=1 1^0=1 1^1=0 |
取反运算符 ~
定义:参加运算的一个数据,按二进制进行“取反”运算。
1 | ~1=0 ~0=1 |
1 | ~5 |
1 | ~-6 |
左移运算符 <<
定义:将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)
设 a=1010 1110,a = a<< 2
将a的二进制位左移2位、右补0,即得a=1011 1000。
右移运算符 >>
定义:将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。
例如:a=a>>2 将a的二进制位右移2位,左补0 或者 左补1得看被移数是正还是负。
浮点数转二进制
举例:22.8125 转二进制的计算过程:
整数部分:除以2,商继续除以2,得到0为止,将余数逆序排列。
22 / 2 11 余0
11/2 5 余 1
5 /2 2 余 1
2 /2 1 余 0
1 /2 0 余 1
得到22的二进制是10110
小数部分:乘以2,取整,小数部分继续乘以2,取整,得到小数部分0为止,将整数顺序排列。
0.8125x2=1.625 取整1,小数部分是0.625
0.625x2=1.25 取整1,小数部分是0.25
0.25x2=0.5 取整0,小数部分是0.5
0.5x2=1.0 取整1,小数部分是0,
得到0.8125的二进制是0.1101
结果:十进制22.8125等于二进制00010110.1101