前言
大家应该都知道,整数包括负数,零,和正数。在Java中,基本类型中byte(8位)、short(16位)、int(32位)、long(64位)属于整数,并且没有无符号数,均是有符号的。对于计算机来说,它只认识二进制,也就是0和1,那我们开发过程中所使用的整数(大多是10进制)在计算机中是怎么存储成二进制的呢?本文将详细解读Java整数在计算机中的存储原理。
整数的编码方式
整数的编码分为原码、反码和补码。计算机使用的是补码的存储方式。它们的定义如下:
原码:在数值前面增加了一位符号位(即最高位为符号位),该位为0表示正数,该位为1表示负数,其余位表示数值的大小。
反码:正数的反码与其原码相同。负数的反码是对其原码逐位取反,但符号位除外。
补码:正数的补码与其原码相同,负数的补码就是对该负数的反码加1。
因为计算机是以补码来存储整数的,所以补码就显得很重要。那么如何计算整数的补码呢?下面以具体例子来说明。
100 -> 原码/反码/补码:01100100
0 -> 原码/反码/补码:00000000
-100 -> 原码:11100100 -> 绝对值:01100100 -> 取反加1:10011011+1 -> 补码:10011100
1 -> 原原码/反码/补码:00000001
-1 -> 原码:10000001 -> 绝对值:00000001 -> 取反加1:11111110+1 -> 补码:11111111
127 -> 原码/反码/补码:01111111
-128 -> 绝对值:10000000 -> 取反加1:01111111+1 -> 补码:10000000
从定义可以看出,正数的补码,反码,原码相同。0的补码就是本身。
那么负数的原码和补码如何转换呢?
- 已知一个负数求补码方法:绝对值原码按位求反加1。
- 已知负数补码求负数方法:符号位不变,其他位按位求反加1。
有了原码,为什么还要用补码来存储整数
下面我们以基本类型byte为例来说明这个问题,
因为byte是8bit,从理论上讲,如果利用充分的话,那么它应该可以表示2^8=256个数值。
如果采用原码来存储
1 - 1 = 1 + ( -1 ) =(00000001) + (10000001) = (10000010) = -2 这显然是不正确的。
原码在两个整数的加法运算中是没有问题的,问题出现在带符号位的负数身上。原码无法满足运算要求。
如果采用反码来存储
反码的取值空间和原码相同且一一对应。所以反码的表示范围位:-127到-0到+0到+127,共255个数值,这里我们把+0和-0都当成0对待。
下面是反码的减法运算:
1 - 1 = 1 + ( -1 )= (00000001) + (11111110) = (11111111) = ( -0 ) 没问题。1 – 2 = 1 + ( -2 ) = (00000001) + (11111101) = (11111110) = ( -1 ) 正确。
反码的问题出现在(+0 : 00000000)和(-0 : 11111111)上,因为在人们的计算概念中零是没有正负之分的。
如果采用补码来存储
1 - 1 = 1 + (-1) = (00000001) + (11111111) = (00000000) = 0 正确。 1 – 2 = 1 + (-2) = (00000001) + (11111110) = (11111111) = ( -1 ) 正确。
00000000 表示0,10000000(0的反码:01111111加上1) 表示 -128
通过补码的运算,可以看出补码的设计目的是:
- 使符号位能与有效值部分一起参加运算,从而简化运算规则。
- 使减法运算转换为加法运算,进一步简化计算机中运算器的线路设计。
- 此外,在补码中用-128(10000000)代替了-0,所以没有+0和-0之分,符合常理,所以补码的表示范围为: -128到127共256个。
- 补码刚好是充分利用了byte的8bit空间。
结语
本文详细解读了Java整数(包括正数和负数)在计算机中的存储原理,并且以byte基本数据类型为例阐述了采用补码的形式存储数值的好处,希望大家看完本篇后能够弄清楚整数在计算机中的存储原理。