目录
算法通关村 —— 移位运算解析
1. 计算机中的数字
2 移位运算
3 移位运算与乘除法的关系
在进行移位运算的学习前,我们先了解下数字在计算机中的表示。
机器数:数字在计算机中以二进制的形式表示,叫做这个数的机器数。机器数带符号,计算机中最高位的数存放符号,正数为0,负数为1。比如,十进制的+5,计算机字长为8位,转换成二进制就是00000101。如果是-3,就是10000101。
真值: 由于机器数第一位是符号,为了区分机器数与真正的数值,将带符号位的机器数对应的真正数值称为机器数的真值。例:00000010真值为2,10000010真值为-1。
原码: 就是符号位加上真值的绝对值,即用第一位表示符号,其余位表示值。例:
[ +1 ] 原 = 00000001 [ -1 ] 原 = 10000001
第一位为符号位,所以8位2进制数取值范围为[ 11111111, 01111111], 即 [-127, 127]。
反码:正数的反码是其本身,负数的反码是在原码基础上,符号位不变,其余位取反。
例:[ +1 ] = [ 00000001 ] 原 = [ 00000001 ] 反 [ -1 ] = [ 10000001 ] 原 = [ 11111110 ] 反
补码:正数的补码是其本身,负数的补码是在其原码的基础上,符号位不变,其余各位取反,最后+1。
例: [ +1 ] = [ 00000001 ] 原 = [ 00000001 ] 反 = [ 00000001 ] 补
[ -1 ] = [ 10000001 ] 原 = [ 11111110 ] 反 = [ 11111111 ] 补
移位运算其实就是类似将原始数字除以2或者乘以2的运算,具体计算形式可能有所不同。
按照 移位方向 可以分成 左移 和 右移
按照 是否带符号 可以分成 算术移位 和 逻辑移位
算术移位:用高位补最高位
逻辑移位: 高位补0
左移运算:符号是 <<, 将全部二进制位向左移动若干位,高位丢弃,低位补0。对于左移运算,算术移位和逻辑移位相同。
右移运算:符号是 >>, 将全部二进制位向右移动若干位,低位丢弃,高位的补位由算术移位或逻辑移位决定: 算术移位时,高位补最高位;逻辑移位时,高位补0.
示例:
1)29的二进制表示为00011101,29左移2位后二进制表示为01110100,结果为116
2)50的二进制表示为00110010,50右移2位后二进制表示位00001100,结果为12,由于正数或0的第一位都位0,所以对于0和正数,无论是算术右移还是逻辑右移,其高位都是补0,所以两者右移的结果是相同的
3)-50的二进制表示为10110010,补码为11001110,-50算术右移两位后二进制表示为 11110011,对应的二进制原码为10001101,即为-13;-50逻辑右移2位二进制表示为00110011,正数的补码是其本身,故原码同为00110011,即为51
所以右移运算中算术移位和逻辑移位是不同的,计算机内部的右移运算采取哪一种呢?
⚪ 对于C/C++, 数据类型包含有符号类型和无符号类型,分别用关键字signed和unsigned声明。不使用关键字时默认为有符号类型。对于有符号类型,右移运算为算术右移,对于无符号运算,右移运算为逻辑右移。
⚪ 对于Java,不存在无符号类型,所有的表示整数的类型都是有符号类型,因此需要区分算术右移和逻辑右移。在Java中,算术右移符号是 >>, 逻辑右移符号是 >>>。
由上可知,移位运算就是令指定数乘或除以2的若干次幂,所以通过移位运算可以实现乘除操作。由于计算机底层的一切运算都是基于位运算实现的,因此使用移位运算实现乘除法的效率高于直接乘除法。
左移运算对应乘法运算。将一个数左移k位,等价于将该数乘以2^k。例如,29左移两位结果为116,等价于29 × 4。而当乘数不是2的整数次幂时,则可以将乘数拆成若干项2的整数次幂之和,例如: a × 6 等价于a × (4 + 2), 即(a<<2)+(a<<1)。所以对任意整数,乘法运算都可以用左移运算实现,但需注意溢出的情况,例如在8位二进制表示下,29左移3位就会出现溢出。
算术右移运算对应除法运算。将一个数右移k位,相当于将这个数除以2^k。例如,50右移2位结果为12,等价于50 / 4,结果向下取整。
注意:对于0和整数,可以说将一个数算术右移k位和将这个数除以2^k等价,因为整数除法是向0取整,右移运算是向下取整,也是向0取整。但是对于负数,就不成立了,整数除法是向0取整,而右移运算是向下取整,但注意此时为负数向下取整,所以两者就不同了。例如(-50)>> 2 结果是-13, 而(-50)/ 4 结果是-12,两者不相等。但是不用担心,算法出题早就考虑了这一点,所以大部分算法题都将测试数据限制在正数和0的情况,因此可以放心地左移或者右移。