原码
一个数的二进制表现形式就是这个数的原码。
比如:
3 -> 00000011
其中,原码的最高位是符号位,代表数据的正负,0代表正,1代表负
对于正数的计算,使用原码运算不会有任何问题,例如
3+2=5
00000011
+00000010
-----------
00000101
但如果有负数参与计算,结果就会大跌眼镜,例如
-3+2=-1
10000011
+00000010
-----------
10000101
转化为十进制,结果等于-5,这显然不是正确结果。
为了解决负数的计算问题,反码和补码就诞生了
反码
-
正数的反码与原码相同
-
负数的反码是该数的绝对值的原码按位取反,即0变成1,1变成0,但符号位不变
补码
- 正数的补码和原码相同
- 负数的补码是该数的反码,然后加1
知道了反码和补码的概念之后,我们还需要知道一个很重要的基础知识
计算机是以一个数的补码来进行计算和存储的。
因为正数的补码、原码、反码都是一样的,所以,按照原码运算是没有问题的,但是负数的补码跟原码不一样,所以上面我们计算-3+2的思路其实是错误的,正确计算过程应该先将两个数的补码计算出来,然后通过补码进行运算,得到的结果再转化成十进制,具体过程如下:
1、先表示出-3的反码,即3的原码按位取反,符号位不变
3的原码为
00000011
取反后为
01111100
恢复符号位
11111100
2、再表示出-3的补码,即-3的反码,然后加1
反码上面已经运算得到为
11111100
加1后变为
11111101
3、将-3和2的补码进行加法运算
11111101
+00000010
----------
11111111
注意这里得到的是结果的补码,很显然是一个负数,因为高位为1,这时我们要将补码还原成原码,就是将补码的计算过程反过来执行一遍
先取反(包括符号位)得到
00000000
再加1,为
00000001
最后加上负数符号位1,为
10000001
转化为十进制即为-1,所以-3+2=-1,结果正确
实战演练
我们知道,在java中,byte的取值范围是[-128,127],那么你有没有想过,这两个范围值是怎么计算出来的呢
首先,因为一个byte数占一个字节,一个字节占8位,其中第一位为符号位,0代表正数,1代表负数,所以,byte在计算机中的最大值为01111111,注意,因为计算机存储的是补码,所以这个01111111是最大值的补码,并不是原码,然后需要根据补码计算出原码,而正数的原码和补码相同,所以原码也为01111111,转化为十进制即为127;
同理,byte在计算机中的最小值为10000000,有很多人想不通,难道不是11111111吗,这里请注意,这种想法实际上是现实中的思维,在计算机思维中,符号位和数值位是独立的,每一位要么取1,要么取0,数值位中,求最大则取1,求最小则取0;符号位中,求最大则取0(因为0代表正数),求最小则取1(因为1代表负数),所以计算机认为10000000才是byte中最小的数(因为符号位和数值位均取最小值),再根据补码计算出原码(即求出该数的十进制值),对补码取反后加1,10000000取反为01111111,即127,再加1为128,加上补码前面的符号位,即为-128。
小结
关于原码、补码、反码,属于计算机的基础知识,在位运算时我们会用到,但实际开发过程中很少使用位运算,实际上位运算比四则运算要快很多,掌握此知识点能给我们的优化思路提供新的方向。