IEEE 754-2008 标准(二进制)
计算机中的浮点数(一)-CSDN博客文章浏览阅读188次,点赞12次,收藏12次。这篇文章介绍了计算机系统中浮点数的正式定义,关于浮点数的介绍总共分两篇文章,这是第一篇。https://blog.csdn.net/daduzimama/article/details/141902866
IEEE 754的参数说明:
书接上文,IEEE 754-2008标准(以下简称标准754)对底(radix)β和指数e(exponents)的最小/大值和做了进一步规定。其中:
1,底数β只能是2或者是10,也就是说该标准只支持二进制和十进制
2,指数e的极限范围必须符合:
该标准定义了一些interchange formats交换格式,这些交换格式对所有的比特位都进行了编码,使得他们彼此之间能够在不同平台之间进行无损的数据交换。在所有的交换格式中标准定义了5个basic formats基础格式: 三个32位,64位和128位的二进制格式,以及两个64位和128位的十进制格式。
浮点数的编码方式:
如上图所示,浮点数的编码使用了1个符号位(sign)S、一个宽度为位的指数字段E,以及一个p−1位有效数字的尾数(trailing significand)字段T。我们来回顾一下“有效数字的尾数(或者简称尾数)”是什么意思?在上一篇文章中,介绍了浮点数的正式定义后,又介绍了正规数和次正规数。其中提到了“如果有一种方法,特别是在指数字段中。。。隐藏位约定(leading bit convention/implicit bit convention/hidden bit convention)”。
事实上,在标准754中就使用了这种方法。在二进制浮点数的表示中,对常规数(normal number)而言,他的有效数字(significand)的第一位必然是“1”。而对于相对少见的数(sub-normal number)而言,他的有效数字(significand)的第一位必然是“0”。因此,不论表示成normal number还是sub-normal number都没必要存储这一位,而是把更多的空间用来存储其他有用信息。即,只储小数点后面的p−1位:这就是有效数字的尾数(在有的地方也称“分数”)。这种不存储有效数字最左边一位的方式被称为“前导位约定(leading bit convention)”或“隐藏位约定(hidden bit convention)”。
简而言之,如果有效数位是24位,实际只储存23位。
二进制浮点数的编码方式:
基于上面的三个不同字段,E是指数字段的位组成的整数,T为由尾数字段的位组成的整数,S为表示符号位的整数,一个数用二进制浮点数来表示,可表示为(S,E,T)的函数,具体编码方式如下,主要是由E和T这两个字段所共同决定的:
1,如果(即,指数字段全为1)且,则浮点数为NaN(not a number)。这个NaN是quiet NaN或者signaling NaN二者中的一种。其中,quiet NaN特指无效操作(例如,这种操作)。而signaling NaN则特指操作异常。
2,如果(指数字段全为1)且(尾数字段全为0),则专门用于表示正无穷或者负无穷:
3,如果且,表示带符号的零:
4,如果且,这种情况专门用于表示Sub-normal number相对少见的数:
5,只有E在之间的时候,才被用于表示normal number常规数:
其中,b表示偏移量(bias),且。对于不同的交换格式b的值也随之而改变。
单精度浮点数的常规数表示(normal number)
就二进制单精度而言,暂且先不考虑前面提到的NaN,正负无穷大等等,我们先用一个例子来看看32位单精度浮点数的表示方法。
首先,根据标准754给定的参数单精度浮点数共有32bit:
他们分别是1bit的符号位,8bit的指数,23bit的尾数(不需要保存隐藏位,隐藏位由指数字段指出)。精度p=24,,大约相当于是7.225个十进制数字的精度。
假设一个数N在32位浮点数的系统中被表示/编译(encoding)为:
1,从左至右逐一解码,先看符号位S,符号位为0,根据公式得到
这说明N是一个大于等于0的数(不一定是正数,可能是0)。
2,其次再看指数E,指数部分既不是也不是,参考标准754所定义的表格,这说明N是一个常规数(normal number)。同时,这也指出了尾数的隐含位一定是1(sub-normal number的隐含位是0)。
表示常规数要使用标准754定义的公式:
再度参考标准可知,在表示常规数时需要用到偏移量b(bias),且。因此,实际指数应该是:
其中:
Python code:
1*(2**6)+1*(2**5)+1*(2**3)+1*(2**1)+1*(2**0)
即:
Python code:
2**(-20)
3,有效数字的尾数(trailing significand)+隐含位(hidden bit)得到真正的有效数字:
这里我们分别把二进制数的整数部分和小数部分转化到十进制,对于小数部分而言有:
对于整数部分而言:
最终得到:
Python代码:
1/(2**2)+1/(2**4)+1/(2**6)+1/(2**8)+1/(2**10)+1/(2**12)+1/(2**14)+1/(2**16)+1/(2**18)+1/(2**20)+1/(2**22)
4,合并所有计算结果,最终得到十进制的原始数N
单精度浮点数相对少见的数的表示(sub-normal number)
假设一个数N在32位浮点数的系统中被表示/编译(encoding)为:
1,还是从左至右逐一解码,先看符号位S,符号位为1,根据公式得到
这说明N是一个负数。
2,再看指数E。参考标准754所定义的表格,指数部分E是且有效数字的尾数T不为0,这说明N是一个相对少见的数(sub-normal number)。同时,这也进一步指出了尾数的隐含位一定是0。
表示sub-normal number要使用标准754定义的公式:
其中,代入上式得到:
3,有效数字的尾数(trailing significand)+隐含位(hidden bit)得到真正的有效数字:
把二进制小数转换为十进制得到:
Python代码:
1/(2**2)+1/(2**3)
4,合并所有计算结果
Python代码:
-1*2**(-126)*0.375
单精度normal number所能表示的极限
根据标准754定义的用于表示normal number的公式:
最小的正normal number(the smallest positive normal number)是由规定范围内所允许的最小指数E和最小的尾数T所决定的,即令E=00000001,T等于0时的数:
最大的正normal number(the largest finite floating-point number)是由规定范围内所允许的最大的指数和最大的尾数确定的,即,令E=11111110,T为全1时的数:
又因为在二进制中:
故而上面的计算公式可改为:
最终得到:
单精度sub-normal number所能表示的极限
sub-normal number常用于表示非常小的数,所以一般情况下,他的所能表示的极限往往特指他能表示的最小的数。根据标准754定义的用于表示sub-normal number的公式:
最小的正normal number(the smallest positive sub-normal number)由最小的指数和最小的尾数确定,其中,E=00000000,T为000...1的数:
把二进制转换为十进制后,最终得到:
参考:
双精度浮点数的常规数表示
首先,根据标准754给定的参数双精度浮点数共有64bit:
他们分别是1bit的符号位,11bit的指数,52bit的尾数(不需要保存隐藏位,隐藏位由指数字段指出)。精度p=53,,大约相当于是15.96个十进制数字的精度。
浮点数的表示太长了,书上没有给例子我这里也不再给了。具体都可以参考32bit单精度的转换方法。
双精度normal number所能表示的极限
根据标准754定义的用于表示normal number的公式:
最小的正normal number(the smallest positive normal number)是由规定范围内所允许的最小指数E和最小的尾数T所决定的,即令E=000...01,T等于0时的数:
最大的正normal number(the largest finite floating-point number)是由规定范围内所允许的最大的指数和最大的尾数确定的,即,令E=111...10,T为全1时的数:
双精度sub-normal number所能表示的极限
sub-normal number常用于表示非常小的数,所以一般情况下,他的所能表示的极限往往特指他能表示的最小的数。根据标准754定义的用于表示sub-normal number的公式:
最小的正normal number(the smallest positive sub-normal number)由最小的指数和最小的尾数确定,其中,E=00...000,T为000...01的数:
把二进制转换为十进制后,最终得到:
By the way:
算到这个地方的时候,我发现python基本上已经算不出来了,需要引入“decimal”模块进行高精度运算。
python code:
import decimal
# 设置精度为50位
decimal.getcontext().prec = 50
# 计算 1/(2**1074)
result = decimal.Decimal(1) / decimal.Decimal(2**1074)
print(result)
(全文完)
--- 作者,松下J27
参考文献(鸣谢):
1,《Handbook of floating-point arithmetic》 ,second edition, Jean-Michel Muller
2,《Elementary Functions Algorithms and Implementation》,Jean-Michel Muller
3,https://en.wikipedia.org/wiki/Floating-point_arithmetic#IEEE_754:_floating_point_in_modern_computers
4,https://en.wikipedia.org/wiki/Single-precision_floating-point_format
5,https://en.wikipedia.org/wiki/Double-precision_floating-point_format
版权声明:所有的笔记,可能来自很多不同的网站和说明,在此没法一一列出,如有侵权,请告知,立即删除。欢迎大家转载,但是,如果有人引用或者COPY我的文章,必须在你的文章中注明你所使用的图片或者文字来自于我的文章,否则,侵权必究。 ----松下J27