为什么Java中int型数据取值范围是[-2^{31}, 2^{31}-1]

一、数字在计算机中的存储形式

数字 计算机中的存储位
2 31 -2^{31} 1000 0000 0000 0000 0000 0000 0000 0000
2 31 + 1 -2^{31} + 1 1000 0000 0000 0000 0000 0000 0000 0001
2 31 + 2 -2^{31} + 2 1000 0000 0000 0000 0000 0000 0000 0010
2 31 + 3 -2^{31} + 3 1000 0000 0000 0000 0000 0000 0000 0011
2 31 + 4 -2^{31} + 4 1000 0000 0000 0000 0000 0000 0000 0100
2 31 + 5 -2^{31} + 5 1000 0000 0000 0000 0000 0000 0000 0101
2 31 + 6 -2^{31} + 6 1000 0000 0000 0000 0000 0000 0000 0110
2 31 + 7 -2^{31} + 7 1000 0000 0000 0000 0000 0000 0000 0111
2 31 + 8 -2^{31} + 8 1000 0000 0000 0000 0000 0000 0000 1000
8 -8 1111 1111 1111 1111 1111 1111 1111 1000
7 -7 1111 1111 1111 1111 1111 1111 1111 1001
6 -6 1111 1111 1111 1111 1111 1111 1111 1010
5 -5 1111 1111 1111 1111 1111 1111 1111 1011
4 -4 1111 1111 1111 1111 1111 1111 1111 1100
3 -3 1111 1111 1111 1111 1111 1111 1111 1101
2 -2 1111 1111 1111 1111 1111 1111 1111 1110
1 -1 1111 1111 1111 1111 1111 1111 1111 1111
0 0 0000 0000 0000 0000 0000 0000 0000 0000
1 1 0000 0000 0000 0000 0000 0000 0000 0001
2 2 0000 0000 0000 0000 0000 0000 0000 0010
3 3 0000 0000 0000 0000 0000 0000 0000 0011
4 4 0000 0000 0000 0000 0000 0000 0000 0100
5 5 0000 0000 0000 0000 0000 0000 0000 0101
6 6 0000 0000 0000 0000 0000 0000 0000 0110
7 7 0000 0000 0000 0000 0000 0000 0000 0111
8 8 0000 0000 0000 0000 0000 0000 0000 1000
2 31 8 2^{31} - 8 0111 1111 1111 1111 1111 1111 1111 1000
2 31 7 2^{31} - 7 0111 1111 1111 1111 1111 1111 1111 1001
2 31 6 2^{31} - 6 0111 1111 1111 1111 1111 1111 1111 1010
2 31 5 2^{31} - 5 0111 1111 1111 1111 1111 1111 1111 1011
2 31 4 2^{31} - 4 0111 1111 1111 1111 1111 1111 1111 1100
2 31 3 2^{31} - 3 0111 1111 1111 1111 1111 1111 1111 1101
2 31 2 2^{31} - 2 0111 1111 1111 1111 1111 1111 1111 1110
2 31 1 2^{31} - 1 0111 1111 1111 1111 1111 1111 1111 1111

二、计算机存储数字的原理

2.1.原码

int型数据在计算机中以二进制存储,一个int型数据占4个字节,一个字节占8位,一共32位。
(1)第一位是标志位,标志位为0表示正数,标志位为1表示负数。
(2)剩余的31位是用来表示数字部分的

2.2.补码

在计算机中,数字以补码存储。正数的补码是其本身,负数的补码是除标志位外,其他位按位取反再加一。

2.3.补码的特性

1、一个负整数(或原码)与其补数(或补码)相加,和为模。
2、对一个整数的补码再求补码,等于该整数自身。
3、补码的正零与负零表示方法相同。


三、两个例子理解补码的计算方式

3.1、第一个例子:7的存储形式

3.1.1.原码

(1)7是正数,所以标志位为0
(2)剩余的31位表示数字部分:000 0000 0000 0000 0000 0000 0000 0111
所以7的原码是:
0000 0000 0000 0000 0000 0000 0000 0111

3.1.2.补码

正数的补码与原码一样,所以7在计算机中的存储形式为:
0000 0000 0000 0000 0000 0000 0000 0111

3.2、第二个例子:-7的存储形式

3.2.1.原码

(1)-7是负数,所以标志位为1
(2)剩余的31位表示数字部分:000 0000 0000 0000 0000 0000 0000 0111
所以-7的原码是:
1000 0000 0000 0000 0000 0000 0000 0111

3.2.2.补码

负数的补码是除标志位外,其他位按位取反再加一。所以-7在计算机中的存储形式为:
1111 1111 1111 1111 1111 1111 1111 1001


四、为什么int型数据取值范围是 [ 2 31 , 2 31 1 ] [-2^{31}, 2^{31}-1]

由原理可知,计算机存储数字时,第一位是标志位,只有31位用来存储数字的值。所以最大表示的正数为0111 1111 1111 1111 1111 1111 1111 1111,即: 2 31 1 2^{31}-1
对于负数,当存储数字的31位均为1时,值为 2 31 1 2^{31}-1 ,加上标志位1,此时的负数原码为:
1111 1111 1111 1111 1111 1111 1111 1111
对应的补码为:
1000 0000 0000 0000 0000 0000 0000 0001
用类似的表示方法可以表示出 [ 2 31 1 , 2 31 1 ] [-2^{31}-1, 2^{31}-1] 中的数,但是如果这么计算的话,1000 0000 0000 0000 0000 0000 0000 0000 这个数字就被浪费了。对于计算机宝贵的内存,浪费是绝对不允许的,所以1000 0000 0000 0000 0000 0000 0000 0000这个数字就被规定为表示 2 31 -2^{31} 。所以int型数据取值范围是 [ 2 31 , 2 31 1 ] [-2^{31}, 2^{31}-1]


五、为什么要用补码

5.1.类比:时钟使用补码计算加减

例如:时钟的计量范围是0~23,所以时间的模等于24。假设当前时针指向17点,而准确时间是9点,调整时间可有以下两种方法:
1.倒拨8小时,即:17 - 8 = 9;
2.顺拨16小时:17 + 16 = 33 ; 33 % 24 = 9
此例中, 16 就是 -8 在 24 进制中的补码表示。用 16 表示 -8 的好处是将减法转为了加法。

扫描二维码关注公众号,回复: 11158740 查看本文章

5.2.计算机使用补码计算加减

如果正数和负数都用原码表示,计算机计算加减法需要做不同的处理。而如果使用补码表示,计算机计算加减法时统一使用加法计算即可,减轻了计算机的负担。

5.2.1.第一个例子:计算 9 + 5

9 的补码表示为:
0000 0000 0000 0000 0000 0000 0000 1001
5 的补码表示为:
0000 0000 0000 0000 0000 0000 0000 0101
两个补码相加,并去掉32位以外的数(本例结果没有32位以外的数):
0000 0000 0000 0000 0000 0000 0000 1110
即得到了结果 14

5.2.2.第二个例子:计算 9 - 5

9 的补码表示为:
0000 0000 0000 0000 0000 0000 0000 1001
-5 的补码表示为:
1111 1111 1111 1111 1111 1111 1111 1011
两个补码相加,并去掉32位以外的数:
0000 0000 0000 0000 0000 0000 0000 0100
即得到了结果 4

5.2.3.第三个例子:计算 2 31 + 1 -2^{31}+1

上文说道,为了不浪费内存,1000 0000 0000 0000 0000 0000 0000 0000这个数字被规定为表示 2 31 -2^{31} ,事实上这个数字表示 2 31 -2^{31} 并不是凭空规定的,它也符合补码的加减规则。
2 31 -2^{31} 的补码表示为:
1000 0000 0000 0000 0000 0000 0000 0000
1 的补码表示为:
0000 0000 0000 0000 0000 0000 0000 0001
两个补码相加,并去掉32位以外的数(本例结果没有32位以外的数):
1000 0000 0000 0000 0000 0000 0000 0001
即得到了结果 2 31 + 1 -2^{31}+1


六、拓展

由原理可推导出:
Java中short型整数占16位,取值范围: [ 2 15 , 2 15 1 ] [-2^{15}, 2^{15}-1]
long型整数占64位,取值范围: [ 2 63 , 2 63 1 ] [-2^{63}, 2^{63}-1]


附一:位、字节、字符的区别

  • 位(bit):计算机内部,数据储存的最小单位。如:00110011 是一个八位二进制数
  • 字节(byte):计算机中数据处理的基本单位。通常用大写的 B 表示,1 byte = 8 bit
  • 字符:计算机中使用的字母、数字、字、符号。

在 ASCII 编码中:
1 个英文字母占 1 个字节
1 个汉字占 2 个字节
1 个 ASCII 码占 1 个字节

在 UTF-8 编码中:
1 个英文字符、英文标点占 1 个字节
1 个中文字符、中文标点占 3 个字节

在 Unicode 编码中:
1 个英文字符、英文标点占 2 个字节
1 个中文字符、中文标点占 2 个字节

附二:Java 中八种基本数据类型占用空间

  • boolean:1 字节
  • byte:1 字节
  • short:2 字节
  • int:4 字节
  • long:8 字节
  • char:2 字节
  • float:4 字节
  • double:8 字节

注:

  • 不带后缀的整数默认为 int,不带后缀的小数默认为 double
  • 超出 int 取值范围的整数必须添加后缀 L或者l,表示 long 类型。建议使用L,因为l容易与数字 1 混淆
  • 带有Ff后缀的数都属于 float 类型,带有Dd后缀的数都视为 double 类型
  • 编译器会在编译期检查八种基本类型的取值范围,如果超出了范围会报错
  • int 值可以赋值给所有数值类型;
  • long 值可以赋值给 long、float、double类型,为什么 long 值占 8 个字节可以赋值给只占 4 个字节的 float 呢?这是因为赋值时会自动舍弃精度并转换为科学计数法。如:Long.MAX_VALUE: 9223372036854775807,赋值给 float,变为:9.223372E18
  • float 值可以赋值给 float、double 类型
  • double 值只能赋值给 double 类型

参考文章

1.补码_百度百科:https://baike.baidu.com/item/%E8%A1%A5%E7%A0%81

2.Java中八种基本数据类型占用字节空间总结

3.位、字节、字符的区别

原创文章 67 获赞 68 访问量 6万+

猜你喜欢

转载自blog.csdn.net/AlpinistWang/article/details/87994617
31
31)