原码补码异或与位运算移位知识点
异或,英文为exclusive OR,缩写成xor
异或(xor)是一个数学运算符。它应用于逻辑运算。异或的数学符号为“⊕”,计算机符号为“xor”。其运算法则为:
a⊕b = (¬a ∧ b) ∨ (a ∧¬b)
如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
异或也叫半加运算,其运算法则相当于不带进位的二进制加法:二进制下用1表示真,0表示假,则异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1),这些法则与加法是相同的,只是不带进位,所以异或常被认作不进位加法。
byte、bit
字节(Byte)/比特位(bit)
1 | B是Byte的缩写,B就是Byte,也就是字节(Byte);b是bit的缩写,b就是bit,也就是比特位(bit)。 |
原码、反码、补码
其实数据存储在内存中都是存储的二进制,二进制又可分为原码、反码、补码。最终存储在内存中的是“补码”。
一个正数的原码、反码、补码都是它的二进制表现形式。(无符号数没有原码、反码和补码一说。只有带符号数才存在不同的编码方式。)
1 | 例如:9 == int == 4个字节 == 1个字节等于8位 == 整形有32位 |
二进制的第一位是符号位,0代表整数,1代表负数。
一个负数的原码是首位为1的二进制数。反码是符号位不变,其他位取反。补码是反码加1。
1 | 例如:-9 |
为什么会有原码反码补码?
- 由于最高位是符号位,如果是0就代表正数,1就代表是负数。
- 那么直接存储原码,计算机在计算的时候还需要先判断最高位才能计算,效率比较低
- 为了方便计算机计算,所以有了反码和补码,然后计算机就不需要判断符号位了,只做加法运算就可以了。
计算十进制的表达式:1 - 1 = 0
1 | 1 - 1 == 1 + (-1) |
如果用原码表示,让符号位参与计算,显然对于减法来说是不正确的。这也是计算机内部不以原码表示的原因。
- 为了解决原码做减法的问题,出现了反码:
1 | 1 - 1 == 1 + (-1) |
通常我们用最高的有效位来表示数的符号(当用8位来表示一个整数时,第8位即为最高有效位,当用16位来表示一个整数时,第16位即为最高有效位。)0表示正号、1表示负号,这种正负号数字化的机内表示形式就称为“机器数”,而相应的机器外部用正负号表示的数称为“真值”。将一个真值表示成二进制字串的机器数的过程就称为编码。
- 于是补码出现了,解决了0的符号问题以及两个编码的问题,0的编码就只有+0,-0已经等于-128了。
1 | 1 - 1 == 1 + (-1) |
与、或、异或运算
1.与运算(&)
参加运算的两个数据,按二进制位进行“与”运算。
1 | 运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1; |
2.或运算(|)
参加运算的两个对象,按二进制位进行“或”运算。
1 | 运算规则:0|0=0; 0|1=1; 1|0=1; 1|1=1; |
3.异或运算(^)
参加运算的两个数据,按二进制位进行“异或”运算。
1 | 运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0; |
即:参加运算的两个对象,如果两个相应位为“异”(值不同),则该位结果为1,否则为0。
1 | 例如:9^5可写成算式如下: 00001001^00000101=00001100 (十进制为12)可见9^5=12 |
位、移位运算
一个字节数占 8
位,将 a
,b
转换为二进制:
1 | a = 0000 1111 |
Note:计算机使用补码表示
位与运算符:仅当两个操作数同一下标的值均为 1 时,结果才为 1
1 | a & b = 0000 1111 & 1111 0001 = 0000 0001(补) = 0000 0001(原) = 1 |
位或运算符:只要两个操作数同一下标的值有一个为 1 时,结果就为 1
1 | a | b = 0000 1111 & 1111 0001 = 1111 1111(补) = 1000 0001(原) = -1 |
位异或运算符:只有两个操作数同意下标的值不相等时,结果才为 1
1 | a ^ b = 0000 1111 ^ 1111 0001 = 1111 1110(补) = 1000 0010(原) = -2 |
位取反运算符:按位取反每一位
1 | ~a = ~0000 1111 = 1111 0000(补) = 1001 0000(原) = -16 |
Note 1:byte 或者 short 类型数值进行位运算后,返回的是 int 类型数值(没有找到资料说明在位运算之前是否已经进行了转换,不过先将 a,b 转换为 int 类型二进制再进行计算的结果和上面一致)
Note 2:位运算符的操作不排除符号位
移位运算符
Java 提供了 3 种移位运算符
- 左移运算符(left shift operator):<<
- 右移运算符(right shift operator):>>
- 无符号右移运算符(unsigned right shift operator):>>>
对于移位运算符而言,左侧操作数表示要移动的二进制数,右侧操作数表示要移动的位数
进行移位操作时,需要注意以下几点:
对于 byte 或者 short 类型数值,进行移位操作时,会先转换为 int 类型,然后进行移位(如果是 long 类型,则不变)
对于右侧操作数而言,在进行移位之前,先转换为二进制数(补码)。如果左侧数是 int 类型,则取右侧操作数最右端 5 位数值进行移动;如果是 long 类型数值,则取右侧操作数最右端 6 位数值进行移动
左移运算符:数值位向左移动指定位数
1 | 15 << 3 = 0x0000000f << 3 = 0x00000078(补,原) = 120 |
右移运算符:数字位向右移动指定位数(如果左操作数是正数,高位补 0 ;如果是负数,高位补 1)
1 | 15 >> 3 = 0x0000000f >> 3 = 0x00000001 = 1 |
无符号右移运算符:功能和右移运算符一样,不过无论正负,高位均补 0
1 | 15 >>> 3 = 0x0000000f >>> 3 = 0x00000001 = 1 |
Note 1:移位运算时,从符号位开始操作
Note 2:由结果可知,左移一位相当于乘以2,右移一位相当于除以 2
原码补码异或与位运算移位知识点