位运算及原码、反码、补码
位运算
程序中的所有数在计算机内存中都是以二进制的形式储存的,位运算就是直接对整数在内存中的二进制位进行操作。
位运算是更底层一些的操作,位运算通常相比其他运算有着更高的效率。
运算符号
含义 | C\C++ | Java |
---|---|---|
按位与(AND) | a & b | a & b |
按位或 | a | b | a | b |
按位异或 | a ^ b | a ^ b |
按位取反 | ~ a | ~ a |
左移 | a << b | a << b |
右移 | a >> b | a >> b |
无符号右移 | 无 | a >>> b |
按位与(AND)
按位与处理两个长度相同的二进制数,两个相应的二进位都为1,该位的结果值才为1,否则为0。例如:
0101(十进制5)
AND 0011(十进制3)
= 0001(十进制1)
按位或(OR)
按位或处理两个长度相同的二进制数,两个相应的二进位中只要有一个为1,该位的结果值为1。例如:
0101(十进制5)
OR 0011(十进制3)
= 0111(十进制7)
按位异或(XOR)
按位异或运算,对等长二进制模式按位或二进制数的每一位执行逻辑按位异或操作。
操作的结果是如果某位不同则该位为1,否则该位为0。例如:
0101(十进制5)
XOR 0011(十进制3)
= 0110(十进制6)
取反(NOT)
取反是一元运算符,对一个二进制数的每一位执行逻辑反操作。使数字1成为0,0成为1。例如:
NOT 0111(十进制7)
= 1000(十进制8)
左移
左操作数按位左移右操作数指定的位数,移位后空缺的部分全部填0。
0001(十进制1)
<< 3(左移3位)
= 1000(十进制8)
右移
左操作数按位右移右操作数指定的位数,左边的用原有标志位补充,右边超出的部分舍弃。
1010(十进制10)
>> 2(右移2位)
= 0010(十进制2)
无符号右移
相比 C、C++、JAVA中有一个特有的无符号右移操作符“>>>”,此操作将忽略操作数的符号 同样的还有>>>=。
按位右移补零操作符。左操作数的值按右操作数指定的位数右移,左边部分总是以0填充,右边超出的部分舍弃。
int a = -8;
int b = a >> 3;
int c = a >>> 3;
结果:
a -8 11111111111111111111111111111000
b -1 11111111111111111111111111111111
c 536870911 11111111111111111111111111111
解读:
计算机中所有数都是以补码形式存储的(正数的补码是本身)。
原码:10进制转换成2进制是原码
反码: 正数的反码是本身,负数的反码是负数的原码0变为1,1变为0 (负数求反码时候的符号位不参与变换)
补码: 正数的补码是本身,负数的补码就是负数的反码加一
总结: 正数的原码,反码 ,补码三值合一, 负数的原码,反码,补码不同。
-8
原码 1000 0000 0000 0000 0000 0000 0000 1000
反码 1111 1111 1111 1111 1111 1111 1111 0111
补码 1111 1111 1111 1111 1111 1111 1111 1000
带符号右移(左边的用原有标志位补充,右边超出的部分舍弃),即
1111 1111 1111 1111 1111 1111 1111 1000 变成:
1111 1111 1111 1111 1111 1111 1111 1111
补码 1111 1111 1111 1111 1111 1111 1111 1111
反码 1111 1111 1111 1111 1111 1111 1111 1110
原码 1000 0000 0000 0000 0000 0000 0000 0001 转化为十进制也就是 -1
无符号右移(左边部分总是以0填充,右边超出的部分舍弃),即
1111 1111 1111 1111 1111 1111 1111 1000 变成:
0001 1111 1111 1111 1111 1111 1111 1111
补码 0001 1111 1111 1111 1111 1111 1111 1111 (正数的原码,反码 ,补码三值合一)
反码 0001 1111 1111 1111 1111 1111 1111 1111 (正数的原码,反码 ,补码三值合一)
原码 0001 1111 1111 1111 1111 1111 1111 1111 转化为十进制也就是 53687091
扩展为什么1字节表示的范围是[-128,127]
一个字节8位,即可表示2^8 =256,有符号数那就可以表示2^7 =128个正数、2^7 =128个负数了,但是1000 0000和0000 0000其实都是0,所以理论上它只能表示[-127, 127]。
如果计算机用原码或者反码,会多占用一个表达(+0、-0都会有各自的原码和反码)。但计算机中使用的是补码,+0、和-0的补码都是0000 0000,所以1000 0000可以用来多表达一个数,用来表示-128最合理。
还可以从另外一个角度来理解: -127 的补码是1000 0001.再减去1 就是1000 0000 。那-127-1=-128。
总结:补码不仅解决了符号的表示的问题,还统一了符号位和数值位,使得符号位可以和数值位一起直接参与运算。