java 基本数据类型共有8个。
数据类型 | 大小 | 范围 | 默认值 |
---|---|---|---|
byte(字节) | 8 | -128 ~ 127 | 0 |
short(短整型) | 16 | -32768 ~ 32768 | 0 |
int | 32 | -2147483648 ~ 21474836480 | 0 |
long | 64 | -9233372036854477808 ~ 9233372036854477808 | 0 |
float(单精度浮点型) | 32 | -3.4E38 ~ 3.4E38 | 0.0f |
double(双精度浮点型) | 64 | -1.79E308 ~ +1.79E308 | 0.0d |
Java是一种可以撰写跨平台应用程序的面向对象的程序设计语言,程序不是直接运行在本地操作系统上,而是运行在JVM上,java虚拟机与系统无关,因此不管是32bit还是64bit操作系统,java的基本类型长度固定。
浮点型内存结构
浮点数在内存中是按 科学计数法 来存储的,一个浮点数由三部分组成:符号位S、底数M和指数E。
float
1bit(符号位) | 8bit(指数位) | 23bit(尾数位) |
---|
- 符号位:表示浮点数的正负。0正;1负
- 指数:占用8bit位二进制数,以移码存储,取值范围0~255。因为指数可正可负,存储的数据为元数据+127
- 底数:实际是占用24bit的一个值(1.000 0000 0000 0000 0000 0000),由于最高位始终为1,因此”1.”省略不存储,只存储小数点后面的23bit
举例:
17.625在内存中的存储形式
将十进制的小数转换为二进制的小数的方法:小数*2,取整数部分
- 17转二进制 10001
- 0.625转二进制
0.625 * 2 = 1.25 ————1 (第一位)
0.25 * 2 = 0.5 ————0 (第二位)
0.5 * 2 = 1.0 ————1 (第三位)
因此0.625转二进制为101
17.625的二进制表示:10001.101
小数点向左移动四位,由1.0001101知:底数为0001101,指数为4,必须加127进行修正,因此指数为10000011,
综上:17.625的 float 存储格式就是: 0 10000011 00011010000000000000000
double
1bit(符号位) | 11bit(指数位) | 52bit(尾数位) |
---|
指数范围:-1024 ~ 1023
double的取值范围:-2^1024 ~ 2^1023,也即:-1.79E308 ~ +1.79E308
精度
float和double的精度由 尾数的位数 来决定。
float的底数有23位,2^23=8388608,一共七位,因此单精度浮点数最多能有七位有效数字(根据指数位移动小数点只能补0,并非有效数字),但绝对能保证的为6位,因此float的精度为6~7位有效数字。
double:2^52 = 4503599627370496,一共16位,同理,double的精度为15~16位
浮点数的误差
十进制浮点数转二进制可能存在误差
2.2f转二进制
- 0.2*2=0.4
- 0.4*2=0.8
- 0.8*2=1.6
- 0.6*2=1.2
- 0.2*2=0.4
这样永远也不可能乘到=1.0,得到的二进制是一个无限循环的排列 0011 0011 0011 0011 0011 …取前23位作为float的尾数。以该二进制得到的十进制也不再是2.2,而是2.200000047683716。因此浮点数表示会产生些许误差,double类型同理。
单精度转双精度可能存在误差
原理同上。以上两种情况,若浮点数能用二进制表示,如2.25,则不会存在误差。
移码
移码是对于某一个系列或集合的数使它们都映射到正数轴上去,即在数轴上把数都统一往右移动n位,使得所有的数都不为负数。补码的符号位取反即为移码,一般用做浮点数的阶码。
8bit补码、移码范围
- 8位补码的取值范围是-128~127,为什么不是-127~127?
- 官方定义,nbit补码的取值范围:[-2E(n-1),2E(n-1)-1],补码的零没有正负之分。为什么?
- 8bit移码范围:0~255
8bit补码、移码能表示的所有情况:
8bit补码 | 十进制 | 8bit移码 | 十进制无符号数 |
---|---|---|---|
0 000 0000 | 0 | 1 000 0000 | 128 |
0 000 0001 | 1 | 1 000 0001 | 129 |
… | … | … | … |
0 111 1111 | 127 | 1 111 1111 | 255 |
1 000 0000 | -128 | 0 000 0000 | 0 |
1 000 0001 | -127 | 0 000 0001 | 1 |
1 000 0010 | -126 | 0 000 0010 | 2 |
… | … | … | … |
1 111 1110 | -2 | 0 111 1110 | 126 |
1 111 1111 | -1 | 0 111 1111 | 127 |
问题主要在于,计算机中1 000 0000表示的数值是多少?
转换一下思想,计算机中对于负数的存储:先取绝对值,然后取反加一
-128绝对值128,转换为二进制为1000 0000,取反为0111 1111 ,加一为1000 0000
因此:计算机中补码1000 0000表示的是-128,而不是-0
因此:8bit补码的取值范围-128~127
因此:官方定义,补码的0没有正负区分
为什么不用补码存储浮点数的阶码
- 由于移码都不为负数,所以直接可以由移码的表示形式看出对应数值的大小(这里是把移码当做无符号数来比较大小的,而不必考虑符号,比用补码好多了,补码是要变换成原码再比较的)
- 阶码用移码表示的话,简化了对于“0”的判断,即当移码的各位都为0的话,那么该移码对应的数据是能够表示的数据中的最小值(移码是把数据集合映射到正数轴上,所以0是数据集合中最小值的映射)。
- 用移码来表示阶码的话可以提高表示数据的精度。如果不是采用移码来表示阶码的话,那么当阶码就会出现负值,这样的话当需要把该数据转换成非阶码浮点数据时我们将就需要对尾数进行左移(做乘法运算),这样的话我们会发现最先移去的是尾数的高位,因此这种方法不利于数据的精度表示。而对于才用移码表示阶码的话,我们在转换时是做右移操作,这样最先移去的是最低位,这样比较好地保持了数据的精度。
机器零
- 浮点数尾数为0时,不论其阶码为何值,按机器零处理
- 浮点数阶码小于等于浮点机所能表示的最小阶码时,不论其尾数为何值,按机器零处理。
8bit阶码用补码表示的最小阶码为-128,二进制为1 000 0000
8bit阶码用移码表示的最小阶码为0,二进制为 0000 0000
可以看出移码表示阶码有利于机器中“判0”电路的实现