原码 补码 反码

原码 补码 反码

Posted by WangZiTao on Friday, August 23, 2019

Java数据类型

了解原码、补码、反码之前,我们先看下java的数据类型:

Java基本类型共有八种,它们的取值范围是固定的,不会随着机器硬件环境或者操作系统的改变而改变。实际上,JAVA中还存在另外一种基本类型void,它也有对应的包装类 java.lang.Void,不过我们无法直接对它们进行操作。8 中类型表示范围如下:

  • byte:8位,最大存储数据量是255,存放的数据范围是-128~127之间。

  • short:16位,最大数据存储量是65536,数据范围是-32768~32767之间。

  • int:32位,最大数据存储容量是2的32次方减1,数据范围是负的2的31次方到正的2的31次方减1。

  • long:64位,最大数据存储容量是2的64次方减1,数据范围为负的2的63次方到正的2的63次方减1。

  • float:32位,数据范围在3.4e-45~1.4e38,直接赋值时必须在数字后加上f或F。

  • double:64位,数据范围在4.9e-324~1.8e308,赋值时可以加d或D也可以不加。

  • boolean:只有true和false两个取值。

  • char:16位,存储Unicode码,用单引号赋值。

以上各种类型占用内存:

数据类型类别大小/位大小范围
byte(位)整型8-128~127
short(短整数)整型16-32768~32767
int(整数)整型32-2147483648~2147483647
long(长整数)整型64-9223372036854775808~9223372036854775807
float(单精度)浮点型321.4E-45~3.4028235E38
double(双精度)浮点型644.9E-324~1.7976931348623157E308
char(字符)字符型160~65535
boolean布尔型8true或false

byte占用一个字节,取值范围是-128~127,因为在二进制中,最高位是符号位,0表示正、1表示负,其他位是数据位。 byte共占8个bit,最大值为01111111,转成十进制为127,最小值为10000000,1是符号位,表示负数,转成十进制为128。所以最小值为-128 ,具体为什么是-128?二进制和十进制如何在底层转换?将涉及到原码、反码、补码,我们往下继续看

原码 补码 反码

机器数和真值

我们先了解下数据在计算机中是怎么表示的

机器数

 一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1.比如,十进制中的数 3 ,计算机字长为8位,转换成二进制就是00000011。如果是 -3 ,就是 10000011 。那么,这里的 00000011 和 10000011 就是机器数。

真值

因为第一位是符号位,所以机器数的形式值就不等于真正的数值。 例如上面的有符号数10000011,其最高位1代表负,其真正数值是-3而不是形式值131(10000011转换成十进制等于131)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。 例:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1

原码概念

数值X的原码记为[x]原,如果机器字长为n(即采用n个二进制位表示数据)。则最高位是符号位。0表示正号,1表示负号,其余的n-1位表示数值的绝对值。

数值零的原码表示有两种形式:[+0]原=0000 0000   ,-[0]原=1000 0000.

例子:若机器字长n等于8,则

[+1]原=0000 00001           [-1]原=1000 00001  

[+127]原=0111 1111          [-127]原=1111 1111

[+45]原=0010 1101           [-45]原=1010 1101    

可见,原码,在计算数值上出问题了,当然,你也可以实验下,原码在计算正数和正数的时候,它是一点问题都没有的,但是出现负数的时候就出现问题了。所以才会有我下面将的问题:反码

反码概念

数值X的反码记作[x]反,如果机器字长为n,则最高位是符号位,0表示正号,1表示负号,正数的反码与原码相同,负数的反码 则是其绝对值按位求反

数值0的反码表示有两种形式:[+0]反=0000 0000   ,-[0]反=1111 1111.

例子:若机器字长n等于8,则

[+1]反=0000 00001           [-1]反=1111 1110 

[+127]反=0111 1111          [-127]反=1000 0000

[+45]反=0010 1101           [-45]反=1101 0010  

在看反码计算的问题:

1+(-1)=0   |  (0000 0001)反+(1111 1110)反=(1111 1111)反=(1000 0000)原=【-0】  可以看到,虽然是-0,但是问题还不是很大

1+(-2)=-1 |  (0000 0001)反+(1111 1101)反=(1111 1110)反=(1000 0001)原=【-1】  可以看到,没有问题

-1+(2)=1   |  (1111 1110)反+(0000 0010)反=(0000 0000)反=(0000 0000)原=【0】  可以看到,问题发生了,因为溢出,导致结果变为0了。

所以,看以看到,用反码表示,问题依然没有解决,所以,出现了下面的补码

补码概念

数值X的补码记作[x]补,如果机器字长为n,则最高位是符号位,0表示正号,1表示负号,正数的补码与原码反码都相同,负数的补码则等于其反码的末尾加1。 数值0的补码表示有唯一的编码:[+0]补=0000 0000   ,-[0]补=0000 0000.

例子:若机器字长n等于8,则

[+1]补=0000 00001           [-1]补=1111 1111  [+127]补=0111 1111          [-127]补=1000 0001 [+45]补=0010 1101           [-45]补=1101 0011  

在看补码计算的问题:

1+(-1)=0  |  (0000 0001)补+(1111 1111)补=(0000 0000)补=(0000 0000)原=【0】  可以看到。没有问题

1+(-2)=-1 |  (0000 0001)补+(1111 1110)补=(1111 1111)补=(1000 0001)原=【-1】  可以看到,没有问题

-1+(2)=1  |  (1111 1111)补+(0000 0010)补=(0000 0001)补 =(0000 0001)原=【1】  可以看到,没有问题

通过上面的计算,我们发现,用补码的方式,就不存在在原码和反码中存在的计算问题了。其实,这也是计算机表达带符号整数用补码的原因。如果,你觉得我举得例子太少,缺少代表行,你可以自己试试。不过,放心补码一定是不会存在原码和反码的问题的。

明白了计算机中补数的道理,那么就明白补码的问题了。还是用例子说明:

在计算机中计算十进制 1+(-2)。

1的原码是:0000 0001

-2的原码是:1000 0010

-2的补码是:1111 1110   这个二进制换做无符号的整数大小就是254,而8位二进制数的M=2^8=256。(很多文章中把M写成2^7,这根本就是不对的,根本没有解决符号位的问题)

你发现什么了没,当换成补码后,-2和254就是补数的关系。

也就是1+(-2)  等价于了 1+254了。

这样做,好处在什么地方,你自己都可以看得到:

  1. 利用补数和溢出的原理,减法变成了加法

  2. 符号位不在是约束计算的问题,不会存在原码中的问题了,因为变成补码后,虽然最高位依然是1,但是这个1就不在是最为符号位了,而是作为一个普通的二进制位,参与运算了。

补码计算规则

正数:原码 = 补码 = 反码

负数:反码 = 符号不变,其它位取反

补码 = 反码 + 1

十进制数原码反码补码
850101 01010101 01010101 0101
-851101 01011010 10101010 1011
90000 10010000 10010000 1001
-91000 10011111 01101111 0111

请计算出 -1 + 1: 十进制的 1 原码 : 0000 0000 0000 0001 十进制的 -1 原码 : 1000 0000 0000 0001 十进制的 -1 反码 : 1111 1111 1111 1110 十进制的 -1 补码 : 1111 1111 1111 1111

计算-1+1即得 ,即-1补码+1的补码 1111 1111 1111 1111 + 0000 0000 0000 0001 = 0000 0000 0000 0000 结果即为0


comments powered by Disqus