最近需要在硬件中处理一些浮点型数据,所以就花了一上午时间重新温习了一下浮点型数据的一些规范和标准,又有很多新收获,将他们整理出来。

为了容易理解和对比,以下整理分别列出单精度浮点和双精度浮点的对应情况。

1)定义:

单精度浮点数为32位,符号1位,阶码8位,尾数23位。如下

符号位阶码尾数
1 bit8 bit 23 bit

双精度浮点数为64位,符号1位, 阶码 11位,尾数52位。如下

符号位阶码尾数
1 bit11 bit 52 bit

2)表示方式:

在计算机中我们都是用二进制(非0即1)数据表示,因此以上所说的位数都是指二进制的位数。

符号位表示这个数据的正负,0表示正数,1表示负数;

阶码表示指数部分;

尾数表示小数部分;

我们应该都学过科学计数法,以上描述就是对应科学计数法的表示情况,举个十进制科学计数法的例子:

-1.2304x10^3(真实值为-1230.4)

符号位为1(负数),阶码为3(指数),尾数为1.2304

在十进制的科学计数法中,小数点前面的数字永远不会是0,一定是1~9中的一个数字,这同样适用于二进制的科学计数法,即小数点前面的数字一定是个1(因为二进制非0即1),因此我们就默认了这个情况,在尾数中不包含这个1(称为隐藏位),单精度尾数23 bit(双精度 52 bit)表示的都是小数点后面的数据。(有一种特殊情况后面会讲

对于阶码部分,因为指数也有正指数和负指数之分,对于一个8bit数据,表示范围为0~255;11bit表示范围为0~2047;指数是通过一个移码来表示,即在阶码的基础上减去一个定值偏置(8bit 为  127,11bit 为 1023),因此一个8bit 阶码表示的指数范围为-127~12811bit对应 -1023 ~ 1024),但是在IEEE标准中,上下边界(-127,128,-1023,1024)被保留留作它用(例如溢出情况等),因此可表示的实际指数范围为-126~127 -1022 ~ 1023);

所以一个单精度数据10101100110111100....00110(32bit)表示的数值为:

-1.10111100....00110x2^(01011001-127),其中小数点前面的1是默认的,2和127是十进制表示;

一个双精度数据01010001110011010101110......010010(64bit)表示的数值为:

+1.11010101110......010010x2^(10100011100-1023)

3)最大值、最小值:

我们首先来看单精度的情况,如上所述,一个32bit单精度可表示的

最大正数为 1.111111111....111x2^127(第一个数为二进制),转成十进制为:

(2^24-1)x2^(127-23) = (2^24-1)x2^104,即 3.4028235E+38

同理,可得最小负数为-(2^24-1)x2^104,即-3.4028235E+38

对于64bit双精度:

最大正数为(2^53-1)x2^(1023-52)=(2^53-1)x2^971,即1.79769313486231570E+308

最小负数为-(2^53-1)x2^971,即-1.79769313486231570E+308

重点来了

重点来了

重点来了

“眼尖”的朋友可能已经发现,为什么没说最大负数和最小正数?对咯,因为存在前面所说的某种特殊情况

之前所说的小数点前面那个默认的1,是在科学计数法的前提下来描述一个数据,但是,因为这个1的存在,它影响了我们可以表达的最小正数精度,什么意思呢?我们来考虑一下(单精度情况),当阶码为最小值1(0被保留它用)时,对应的指数为-126,尾数最小值只能全为0,此时这是可以表达的最小正数了,但是由于前面默认的1,所以所表达的最小值为1x2^(-126),即 1.1754943E-38,

但是我们明明可以表达更小的数据的,去掉默认的1,尾数最后一位置1,其他位数为0,对吧?

嗯,IEEE标准也是这样来考虑的,此时的数据称为非规格化数据。

与之对应的就是规格化数据:即小数点前面默认为1,尾数所有数据表示的都是小数点后面的数据,此时称为规格化数据;

当阶码已经为最小值(1,指数为-126)以后,尾数部分包含全部的小数部分,此时是没有隐藏位的,这样的数据称为非规格化数据,通过引入非规格化数据,使得我们可以表达的最小正数又提高了多个量级。

对于单精度(32bit)而言:

规格化:

最小正数为:1x2^(-126),即1.1754943E-38

最大负数为:-1x2^(-126),即-1.1754943E-38

非规格化:

最小正数为:2^(-23)x2^(-126)=2^(-149),即1.401298E-45

最大负数为:-2^(-149),即-1.401298E-45

对于双精度(64bit)而言:

规格化:

最小正数为:1x2^(-1022),即2.2250738E-308

最大负数为:-1x2^(-1022),即-2.2250738E-308

非规格化:

最小正数为:2^(-52)x2^(-1022)=2^(-1074),即4.94065645E-324

最大负数为:-2^(-52)x2^(-1022)=2^(-1074),即-4.94065645E-324

以上

(这里其实还有一个小疑点,就是从规格化数据过度到非规格化数据,是如何过度区分的呢??)

2022年4月5日

填坑时间到(2023年1月12日)

----------------------------我是分割线------------------------------------

----------------------------我是分割线------------------------------------

之前整理这部分浮点数据内容时,还有一个小疑问就是上面的最后一句话,规格化数据到非规格化数据是如何过度的,今天偶然重温了一下“深入理解计算机系统”课程,人家讲解的还是很清晰的,如果理解了我后面要说的内容,就会发现我前面描述的部分文字是有错误或不准确的,当然为了让大家加以区分,我就不修改前面的文字了,知道错误的同学可以在评论区输出,就可以说明你是真的理解浮点数据了,哈哈哈

直接上图:

 

不得不说,搞IEEE标准的这帮子人还是非常聪明的

上面的图中基本上包含了浮点数据的大部分知识点了,图中例子是8bit浮点数据,1bit符号位,4bit阶码(此时的偏置是7),3bit的尾数

定义如下:

当阶码数值全为零(前面所说特殊情况的一种)的时候表示非规格化数据,此时的指数定义为1-7= -6,尾数部分没有隐藏的1,二进制的001就是指0.001即十进制的1/8,此时尾数全零就是指真值为0(浮点的全零刚好对应上了真值0,这也是由于设置指数偏置的一种友好结果),根据图中所示,我们可以看到数值由零递增了非规格化数据可表示的最大值,即阶码全为0尾数为111

当阶码为非零时,表示规格化数据,此时指数定义为 exp-7(exp表示阶码),尾数部分有隐藏的1,二进制000是指1.000即十进制的1,我们发现,非规格化数据与规格化数据完成了完美的平滑过渡(例子中 从7/512到8/512的平滑过渡),不得不说,厉害!厉害!厉害!

完结

课程是CMU 15-213 CSAPP 深入理解计算机系统,B站已经有中文字幕的课程了,为大家附上学习链接 我是课程链接​​​​​​​

 

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐