补码、反码

Java的byte类型取值范围是-128~127为什么负数是128正数到127呢

首先需要了解原码反码和补码

正数的原码反码补码是一样的

[+2]=[00000010]原=[00000010]反=[00000010]补

对于负数来说,它的原码反码补码就不相同

[-2]=[10000010]原=[11111101]反=[11111110]补

最高位表示符号位0代表正数1代表负数,负数计算反码的规则是符号位不变,其余位取反即1变成0,0变成1,补码就是反码再加1

  1. **补码转原码还可以:补码直接取反,然后加 1。**
  • 计算机存储有符号的整数是都是存储它们的补码。Java语言都是有符号位的。
  • 正数和0的补码、反码是本身原码;所以对于正数来说,可以理解为不存在反码和补码。
  • 负数的反码是是符号位不变,其它位取反;补码是在负数的基础上加1(符号位不变)。负数就是矫情啊!
  • 计算机中用补码进行加法运算。

为什么要设计出反码和补码

因为计算机只有加法没有,减法,在做减法运算的时候,可以认为是加上一个负数,这样可以减少计算机电路的复杂度

使用原码进行减法运算会出现问题,例如计算1-1,因为计算机没有加法只有减法,所以计算机自动换算成1+(-1)

1-1=1+(-1)=[00000001]原+[10000001]原=[10000010]原=-2 (符号位也参与运算)

而1-1的实际结果是0

为了解决这个问题,于是使用反码运算

1-1=1+(-1)=[00000001]原+[10000001]原=[00000001]反+[11111110]反=[11111111]反=[10000000]原=-0

通过反码计算的结果是11111111在计算一次反就成原码了,得出的结果是正确的,但是有一个问题是 00000000可以代表+0 10000000可以代表-0,其实是一样的,用2个编码实在是浪费。于是出现了补码解决0的符号以及两个编码的问题

1-1=1+(-1)=[00000001]原+[10000001]原=[00000001]补+[11111111]补=[00000000]补=[00000000]原

这样0用[0000 0000]表示, 而以前出现问题的-0则不存在了.而且可以用[1000 0000]表示-128:但是注意因为实际上是使用以前的-0的补码来表示-128, 所以-128并没有原码和反码表示.(对-128的补码表示[1000 0000]补算出来的原码是[0000 0000]原, 这是不正确的)

使用补码,不仅仅修复了0的符号以及存在两个编码的问题,而且还能够多表示一个最低数。这就是为什么8位二进制,使用原码或者反码表示的范围是[-127127]

总结:反码是为了解决减法运算,补码是为了解决反码产生的+-0的问题

下面来分析为什么byte型的取值范围为什么是-128~127

  1. byte占用8位,每位用0或者1表示,能够表示258(2^8)个数据
  2. 这8位分为符号位(最高位)和数值位(剩余七位),符号位0表示正数,1表示负数。
  3. 按上一步的理解,容易得到(+127:0111 1111、+1:0000 0001、+0:0000 0000、-0:1000 0000、-1:1000 0001、-127:1111 1111),计算机底层定义了+0(0000 0000)就是0,那么“可怜的-0”又该何去何从呢?计算机遇到这个二进制该如何处理呢?总不能把这两个都对应0吧,这显然是资源浪费。
  4. 到这里我们都认为最高位是不参与计算数值的,仅仅是一个符号位,按这种思路byte的八位是无论如何也表示不出-128。而“可怜的-0”又不知道自己代表谁,不得而知“可怜的-0”就是-128。(为什么呢?凭什么呢?你说代表-128就代表-128呀!为什么不能是+128、-250、+250…….)。
  5. 到这里我们已经很粗浅地回答了-128~127中的-128的由来,基本也回答了这个题目。
  6. 可是然而但是——对于程序员来说,上诉的分析【首先】是结论正确,但是过程错了。错误在于——负数的二进制表示是错误的,比如-127:1111 1111,这是不对的。计算机发现了1111 1111会把它认为是-1而不是-127。【其次】没有解释为什么-0最后表示成-128。
  7. 接着从人的思考方式理解下当计算机处理1111 1111的过程,首位是1,自然是负数,而且这是补码,那么对应的原码就是,先减1,变成1111 1110,符号位不变,其他取反,变成1000 0001,也就是-1!所以从10000001到11111111依次表示-127到-1
  8. 最后来解决这-128为什么可以用1000 0000表示

这里我分析的是byte,它就8位。在无符号位的二进制中128的表示为1000 0000。有符号位的情况下byte好像无法表示+128或-128。

如果我们假设byte不是占用8位,而是9位,最高位是符号位。你们-128的表示为1 10000 0000,计算其补码也是1 1000 0000,很神奇吧,一样的。-128补码尾8位就是1000 0000。那就规定1000 0000是-128的补码,而-128是没有原码和反码的,即不能利用1000 0000反推其反码和原码。

  1. 如果你对8步的推导表示不太接受,那么简单就认为计算机规定了1000 0000就是-128,是一种人为设计没有什么道理可以言(据说是印度阿三设计的)。其实这么设计也是很巧妙的,在于:

【其一】对于如果大于8位的有符号整数数据类型,-128的补码尾八位刚好是1000 0000

【其二】比如127(0111 1111)加1(0000 0001)刚好得到-128(1000 0000),-128(1000 0000)加1(0000 00001)等于-127(1000 0001)这样从-128~127的反码首尾相连,形成了一个闭环,就像时钟一样。

【其三】在计算机中减法运算可以转换成加法运算,比如8-1——>8+(-1)——>补码运算:(0000 1000) + (1111 1111) = (0000 0111) 刚好是7。-128+127——>(1000 0000) + (0111 1111) = (1111 1111)刚好是-1,-128的补码完美的适用于减法。

结论

【1】计算机中负数是用补码的形式保存、并用它参与加减法运算的,减法会被转换为加法,计算机中没有加法运算。

【2】反码是为了解决减法运算,补码是为了解决反码产生的±0的问题。参考( https://blog.csdn.net/boatalways/article/details/17027573)

【3】对人而言二进制所代表的值一定是从原码求出的,开头如果是1的二进制,一定要说明其是原码、反码还是补码。

【4】在原码、反码、补码相互转换以及求对应的十进制求值时,符号位是绝不参与的,但是在加减过程中,是参与位运算的。

【5】计算机中规定了-0对应的二进制就是0,那么-0就没有意义了,必须找一个数和它对应。

【6】byte的最小值-128、short的最小值-32768、int的最小值-2147483648都是用对应的-0的原码来进行表示,这是人为规定的、人为规定的、人为规定的。但是这么规定又很巧妙,妙在上述10中的三点。

参考

https://blog.csdn.net/qq_22771739/article/details/84496115
https://blog.csdn.net/boatalways/article/details/17027573
https://blog.csdn.net/zhiwen_a/article/details/81192087