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(单精度) | 浮点型 | 32 | 1.4E-45~3.4028235E38 |
double(双精度) | 浮点型 | 64 | 4.9E-324~1.7976931348623157E308 |
char(字符) | 字符型 | 16 | 0~65535 |
boolean | 布尔型 | 8 | true或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,但是这个1就不在是最为符号位了,而是作为一个普通的二进制位,参与运算了。
补码计算规则
正数:原码 = 补码 = 反码
负数:反码 = 符号不变,其它位取反
补码 = 反码 + 1
十进制数 | 原码 | 反码 | 补码 |
---|---|---|---|
85 | 0101 0101 | 0101 0101 | 0101 0101 |
-85 | 1101 0101 | 1010 1010 | 1010 1011 |
9 | 0000 1001 | 0000 1001 | 0000 1001 |
-9 | 1000 1001 | 1111 0110 | 1111 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