源码就是符号位 + 二级制数值。符号位是第一位,0 表示正数,1 表示负数。
Java 中 byte 类型一字节八位,可以表示 [1111 1111 , 0111 1111],取值 [-127,127].
正数的反码是其本身;负数的反码是在原码的基础上,符号位不变,其他位相反。
如 0000 0001 的反码是 0000 0001;1000 0001 的反码是 1111 1110。
正数的补码是其本身;负数的补码是反码 + 1。
如 0000 0001 的补码是 0000 0001;1000 0001 的补码是 1111 1111。 -128 补码是 1000 0000。
知其然也要知其所以然。为毛计算机要衍生出原码、反码、补码?
一句话为了方便计算机做减法。
计算机中只有加法器没有减法器,如果要做减法相当于加一个负数。比如 5 - 3 当成 5 + (-3),那么 (-3) 怎么表示呢?
在源码中,正数相加是没问题的。比如 0000 0001 + 0000 0001 = 0000 0010,也是十进制的 1 + 1 = 2,但如果是 1 + (-1) 就会变成 0000 0001 + 1000 0001 = 1000 0010,结果是 -2 。
为了解决这个问题,反码出现了。
如果用反码计算,1 + (-1) 是 0000 0001 + 1111 1110 = 1111 1111 ,在反码中 1111 1111 表示 0,这样就做到了 1 + (-1) = 0。
但是还有个问题,反码中 1111 1111 和 0000 0000 都表示 0 ,准确地讲,一个是 -0,一个是 0。为了拯救强迫症患者,补码出现了。
补码怎么解决这个问题呢?
补码将 -0 + 1,即 1111 1111 + 1 = 1 1000 0000,结果超出范围了(第一个 1 表示符号,1000 0000 表示数值),要去除高位,也就是结果为 1000 0000。
生活中有这样的例子,向北绕地球 3/4 圈和向南绕地球 1/4 所在的经纬度一样的;正向旋转 90° 和逆向旋转 270° 位置也是一样的,只看个位的话,5 - 2 和 5 + 8 结果一样的。补码的 -0 + 1 是类似的,超出的高位没有意义去除就好了,同时最后的结果也是正确的。
在刚刚的例子中,地球一圈、360°、10 这些称为摸,3/4 与 1/4、90° 与 270°、2 与 8 和为模的两个数为补数。
补码将 1000 0000 和 0000 0000 区分开,1000 0000 表示十进制的 -128,0000 0000 表示 0。
再扩展一下,加法器是什么?人脑可以迅速计算出 1 + 2 = 3,但计算机怎么计算呢?
计算机芯片有通电与不通电两个档位,通电为高压(一般 5V,表示二进制 1),不通电低压(一般 0V 表示二进制 0),组装成复杂的电路基本三门——与或非门。
与门:A 和 B 都为 1 结果为 1,否则为 0。
或门:只要 A 和 B 中有一个为 1 结果即为 1。
非门:A 为 1 结果为 0,A 为 0 结果为 1。
以上三门可以组装成异或门。
异或门:A 和 B 结果不一样时结果为 1。
加法的本质是给两个本位,计算出新的本位和进位。比如原本位 1 + 1 得到新本位 0 和进位 1 。
把异或门和与门并联就是半加法器,加法器是 N 个半加法器的复杂组装。
好了,我写蒙了,估计你也看蒙了,不是你的问题,我表达不太好,有疑问可以评论,我们互相讨论。
到这里算是对原码、反码、补码稍微深刻点的认识吧。
将二进制数整体向左移动指定位数,右边空位补 0,比如 a << 1 等价于乘以 2 ,但要注意符号位和取值范围。
将二进制数整体向右移动指定位数,左边空位补符号位,比如 a >> 1 等价于乘以 2 ,同样要注意符号位和取值范围。
将二进制数整体向右移动指定位数,左边空位补 0,正数等同于 >> ,负数会因补 0 变为正数。
待续。。。
其实移位运算符这的疑问也挺多的,比如补 0 和补符号位怎么储存的?原码中只有第一位表示符号,如果要补 2 个符号位分么办?为什么移位相当于乘除 2^n?,如果 5 右移一位怎么搞?等等
实在特么写的有点惨(手动捂脸),先把半成品发出来看能不能混几个赞激励下自己哈哈哈哈,溜了溜了,明天把这些问题搞清楚了再补充。
每一次成长,都想与你分享。(小声BB,抽奖在公众号里面。)