IEEE-754标准单精度浮点类型存储概述

java 单精度浮点数转换 java单精度浮点数定义_单精度

folat(32位单精度浮点类型)

java中浮点类型也就是小数类型,浮点类型一共有两种,float和double;float为32位(4字节)单精度浮点类型,double为64位(8字节)双进度浮点类型。jdk默认的浮点类型是double。
java中的小数一直遵循这IEEE754工业标准,Float类型为32位单精度浮点类型,取值范围为 [1.17549435…E-38,3.4028236611111111111…E1038] ∪ [-3.40282366…E1038 , -1.17549435…E-38]。

IEEE754工业标准

前言

众所周知,计算机内部系统实际只能存储二进制数据,我们在计算机中所使用到的文档、图片、影音等数据,实际都是以二进制的数据形式存放在计算机的内存或者硬盘中,不管内存(内存条)还是硬盘,在这里我们且都称之为“内存”,“内存”可以理解为一个大的容器,里面均匀的分配了很多小格子,这些格子只能存放1或者0。
我们日常所使用的小数也不例外,都会以二进制的数据形式存储的到“内存”中;事实上,直到20世纪八十年代,计算机厂商们还在为各自而战,每家厂商都在设计自己的浮点类型数据存储规则,彼此之间都不兼容;直到1985年,IEEE,电气电子工程师学会(Institute of Electrical and Electronics Engineers)提出里IEEE-754工业标准,浮点类型数据存储问题才有了一个通用的工业标准。
IEEE754标准发布于1985年,包括java、C在内许多比较著名的编程语言都在浮点类型数据存储上遵这这个标准;IEEE754标准提供了四项精度规范,其中最常用的是单精度浮点类型双精度浮点类型,单精度浮点类型在内存中占32位bit,双精度浮点类型在内存中占64位bit,所以我们通常提到的32位浮点类型或者单精度浮点类型一般是指32位单精度浮点类型双精度浮点类型也同理;在java中,float类型就是单精度浮点类型,double就是双精度浮点类型。

单精度浮点存储概述

java 单精度浮点数转换 java单精度浮点数定义_数位_02

每个单精度浮点类型在内存中都占32个bit,可以理解为一个长条状的盒子中有32个小格子,这32个小格子划分为三个区域(符号位、指数位、尾数位),且都有顺序,从0-31(从右到左)

  • sign:符号位,表示该浮点类型的正负,0为正,1为负
  • exponent:指数位,8位,容量高达256种,取值范围为[-127 128],表示该浮点类型数据的大小容量,格子越多可存储的浮点类型数据就越大
  • fraction:尾数位,表示该浮点类型数据的精度,格子越多存储的浮点类型数据精度就越高

单精度浮点存储过程

java 单精度浮点数转换 java单精度浮点数定义_java_03

想要将一个十进制小数存储到这32位的小格子中也需要一定存储过程,此存储过程非彼存储过程,我们现在以十进制数8.5为例,一步一步将它“存储”到这32个格子中。

存储符号位

这一步比较简单,前面有有提到过,0表示浮点数为正数,1为负数,我们单凭肉眼就能看出,8.5为正数 所以符号位是0

存储指数位
规范化(normalized)

java 单精度浮点数转换 java单精度浮点数定义_数位_04

要获取这个浮点类型的指数位,我首先需要将它转换为二进制数,整数“除二进余 取倒序”,小数“乘二取整 取顺序”,8.5的二进制为1000.1,转换为二进制后可以正式进行规范化。将已经转换为二进制小数的1000.1的小数点往左边移动,移动至小数点左边第一位为1为止,这个过程小数点移动了几位就是这个浮点类型的指数,1000.1移动3位变成1.0001,1.0001是它的尾数,3就是这个浮点类型的指数,小数点向左移动其实也是将这个数除以2^向左移动的位数,所以1000.1变成1.0001,其实是除以2的3次方的到的结果,这个过程就是规范化(normalized)

java 单精度浮点数转换 java单精度浮点数定义_java 单精度浮点数转换_05


规范化也会出现小数反向移动的情况,如果浮点类型没有整数,例如十进制数0.5的二进制形式0.1,此时需要向右边反向移动1位,即为1.0,-1就是他的指数,移动后的尾数第一位始终为1(因为它是向第一位1的位置移动),IEEE-754没有必要将它存储,也不打算单独再分配一个尾数的符号位,于是就有了偏移量,单精度浮点类型的偏移量位127,双精度浮点类型为1023,之前提到过指数位只能存储[-127,128],假设存储一个非常小的单精度浮点类型数,规范化时小数点已经需要向右移动127位,即它的指数位-127,加上单精度浮点类型的偏移量127后,也不过是0,它依然是一个整数;所以这个偏移量存在的必要,就是为了确保它在有限的指数位取值范围中保持整数。回归主题,存储指数为3的浮点类型8.5,它的指数加上偏移量后(3+127)为130,转换为二进制就是1000 0010

java 单精度浮点数转换 java单精度浮点数定义_java 单精度浮点数转换_06

指数位只能存八位,其实也会存在计算出的二进制数不足八位的情况,例如119,二进制为1110 111,这是不足八位的,但是也要存储到格子中,所以我们需要补零操作,我们需要在前面补零,切记整数二进制后面补零会影响数值,而小数二进制前面补零会影响数值,1110 111补零为 0111 0111。

java 单精度浮点数转换 java单精度浮点数定义_java_07

存储尾数位
隐藏高位

java 单精度浮点数转换 java单精度浮点数定义_单精度_08

之前有提到过,规范化后的二进制小数就是它的尾数,1.0001就是尾数,规范化会将小数点左边第一位1移动,所以尾数前面第一位始终都是1.,这个1.是可以省略的,所以尾数有变成了0001

低位补零

java 单精度浮点数转换 java单精度浮点数定义_数位_09

尾数位占用23位,没有达到23位时,需补足23位,尾数位存储的都是小数,所以它只能在后面补零,补完之后的尾数是 0001 0000 0000 0000 0000 000

Result

java 单精度浮点数转换 java单精度浮点数定义_数位_10

最后再拼接一下

  • 符号位:8.5为整数,所以符号位为 0
  • 指数位:1000.1左移3位变成尾数 1.0001 ,3+127=130,130二进制为 1000 0010
  • 尾数位:尾数位1.0001,藏高补低后为 0001 0000 0000 0000 0000 000
  • 结果:十进制数8.5的单精度浮点类型存储格式为 0 1000 0010 0001 0000 0000 0000 0000 000