预备知识
在计算机中,数据以二进制形式储存,整形数也不例外,下面我们先复习一下整形数的储存。如果没有特殊说明,下面均以byte(8位)来说明。
原码
符号位 | 数值位 |
---|---|
1位 | 7位 |
符号位
0 = 正数
1 = 负数
数值位
将数转化为二进制储存,高位在前,溢出则丢弃高位,不足左边补0。
反码
将负数的数值位按位取反,例如-20的原码为1 0010100,反码为1 1101011
$$ \begin{matrix} True Form&1&0010100\\ Complement&1&1101011 \end{matrix} $$
补码
正数的补码和原码相同,负数的补码为反码 + 1 (若溢出则丢弃高位)。
移码
数值位于补码相同,但符号位,但符号位相对补码取反,即 0 = 负数,1 = 正数。
为什么要有反码和补码
我们希望有一种表示方法,可以使两个数的加法可以直接相加。
对于正数相加,结果是正确的
$$ \begin{matrix} 16 &&0&0010000\\ 20 &+&0&0010100\\ Ans&=&0&0100100\\ Ans&=&&36 \end{matrix} $$
但对于负数
$$ \begin{matrix} -16 &&1&0010000\\ 20 &+&0&0010100\\ Ans &=&1&0100100 \\ Ans&=&&-36 \end{matrix} $$
正数的原码就是它的二进制表示,结果当然是正确的,但对于负数,情况却不是这样,思考一下我们进行加法运算时,我们需要对正数和负数进行不同的处理,加上一个负数等于减去它的相反数,对于计算机来说,加法运算有两个操作数,也就是有四种情况,一共4种情况,显然计算机这样设计是不方便的。
考虑反码
$$ \begin{matrix} -16 &&1&1101111\\ 20 &+&0&0010100\\ Ans &=&1&0000011\\ Ans&=&&3 \end{matrix} $$
与正确结果已经很接近了,这里是问题是-0导致的。
如果我们给原码+1
$$ \begin{matrix} -16 &&1&1110000\\ 20 &+&0&0010100\\ Ans &=&1&0000100\\ Ans&=&&4 \end{matrix} $$
结果正确。这就是补码。
为什么我们需要移码
负数使用补码表示,我们可以很方便地进行加法,如果我们把符号位取反,我们就可以按位比较两个数的大小了。
位运算
位运算的对象是整型数,正数用原码表示,负数用补码表示。下表是Java语言中的位运算表。
操作符 | 名称 | 描述 |
---|---|---|
& | 与 | 如果相对应位都是1,则结果为1,否则为0 |
丨 | 或 | 如果相对应位都是 0,则结果为 0,否则为 1 |
^ | 异或 | 如果相对应位值相同,则结果为0,否则为1 |
~ | 取反 | 按位取反,0 -> 1,1 -> 0。 |
<< | 左移 | 所有位左移,右边补0。相当于 * 2^n。 |
\>> | 右移 | 所有位右移,左边补符号位。相当于 / 2^n。 |
\>>> | 无符号右移 | 所有位右移,左边补0。相当于无符号数 / 2^n。 |
与
$$ \begin{matrix} -16 &&1&1110000\\ 20 &&0&0010100\\ Ans &=&0&001 0000\\ Ans&=&&16\end{matrix} $$
与运算可以将某些位清零,另外,在判断整型数是奇数还是偶数的时候,可以用来代替 % 2,因为二进制下奇数的末尾是1,number&1的结果是1。
或
$$ \begin{matrix} -16 &&1&1110000\\ 20 &&0&0010100\\ Ans &=&1&1110100\\ Ans&=&&-12\end{matrix} $$
或运算可以将某些位设位1。
异或
$$ \begin{matrix} -16 &&1&1110000\\ 20 &&0&0010100\\ Ans &=&1&1100100\\ Ans&=&&-28\end{matrix} $$
异或运算可以将某些位取反,由于Java语言中没有同或,我们可以用~(n^i)表示同或运算。
非
$$ \begin{matrix} -16 &&1&1110000\\ Ans &=&0&0001111\\ Ans&=&&15\\\\ 20 &&0&0010100\\ Ans &=&1&1101011\\ Ans&=&&-21\end{matrix} $$
事实上,对于逻辑非,在不发生溢出的情况下,总有i + (~i) = -1 成立。
左移
$$ \begin{matrix} -16 &&1&1110000\\ <<1 &=&1&1100000\\ Ans&=&&-32\\\\ 20 &&0&0010100\\ <<1 &=&0&0101000\\ Ans&=&&40\end{matrix} $$
相当于算数左移或者逻辑左移。
右移
$$ \begin{matrix} -16 &&1&1110000\\ >>1 &=&1&1111000\\ Ans&=&&-8\\\\ 20 &&0&0010100\\ >>1 &=&0&0001010\\ Ans&=&&10\end{matrix} $$
相当于算数右移。
无符号右移
$$ \begin{matrix} -16 &&1&1110000\\ >>>1 &=&0&1111000\\ Ans&=&&120\\\\ 20 &&0&0010100\\ >>>1 &=&0&0001010\\ Ans&=&&10\end{matrix} $$
相当于逻辑右移(即不考虑符号)。
但上面有关-16的右移是错误的,无符号位移只对32位(int)或者64位(long)整型起作用,在Java中,无符号右移的两个操作数至少是int,结果也是int。下面是正确的分析。
$$ \begin{matrix} 16 &&00000000\space00000000\space00000000\space00010000\\ -16 &&00111111\space11111111\space11111111\space11111100\\ >>>1 &=&00111111\space11111111\space11111111\space11111100\\ Ans&=&2147483640 \end{matrix} $$