浮点数
摘要
我们说的浮点数包括单精度浮点数和双精度浮点数,浮点数的实现、运算是按照IEEE 754标准进行的,IEEE 754标准是计算机上使用最为广泛的浮点标准。
浮点数的表示
浮点数分为两个组成部分:整数部分(i)和小数部分(f),通常,将浮点数表示为 ± i.f Xβe,其中i.f 称为有效数字,它的数字个数称为有效数字精度,β为基数,e为指数,±表示该浮点数的正负。当其中整数部分i符合:0<i<β时,被称为“规格化数”,否则被称为“非规格化数”。
一个浮点数F在IEEE 754标准中可以用F = (-1)s x M x 2e 的形式表示,说明如下:
(1)符号s决定浮点数是正数(s=0)还是负数(s=1);
(2)有效数字M是二进制小数,M满足1≤M<2或0≤M<1;
(3)指数e是2的幂,它的作用是对浮点数加权(就是指数的阶)。
一个规格化的32位浮点数Df的真值可表示为:Df = (-1)s x (1.M) x 2e e = E -127;
一个规格化的64位浮点数Df的真值为:Df = (-1)s x (1.M) x 2e e = E -1023。
浮点格式
浮点格式是一种数据结构,该结构规定了构成浮点数的各个部分在所属字节上的布局及编码,按照规定,这些字节被划分为3个部分:
(1)一个符号位s,编码符号s;
(2)n位的偏置指数e(en-1…e1e0 其中ei为1或者0),编码为指数E;
(3)k位的小数f(fk-1…f1f0 其中fi 为1或者0),编码为有效数字M。
E的取值范围为0-255(无符号整数),实际数值为e=E-127。E也可以被称为“移码”,而e则被称为“阶码”。
浮点数0.0(或者非常接近0.0的浮点数)的表示方法:e = 0(即en-1…e1e0 全部为零),有效数字M = f = 0. fk-1…f1f0,有效数字的前导有效位为0;
±∞的表示方法:e 的位模式即en-1…e1e0全部为1,小数M = f = 0,即f的位模式全部为零,当s = 1时,为+∞,当s = -1时,为-∞;
规格化数的表示:e 的位模式即en-1…e1e0不全为1,也不全为零,小数部分fk-1…f1f0不全为零,通过调整e来实现,使1≤M<2,此时M = 1. fk-1…f1f0,也就是说:有效数字的前导有效位总是1,因此该位不需显式给出,只需通过指数隐式给出。
单精度格式
在C语言中,单精度浮点型变量存储为 IEEE 32 位,就是4个字节,它的范围在负数的时候是从 -3.402823E38 到 -1.401298E-45,而在正数的时候是从 1.401298E-45 到 3.402823E38。其浮点格式为:23位小数f,8位指数e,1位符号s。将这些字段连续存放在一个32位字里(4字节),并对其进行编码(高字节在后,低子节在前),其中第31位为符号位s,记为s【31】;23:30位包含8位,为指数e,记为e【23:30】;0:22位包含23位,为小数f,记为f【0:22】;如图1所示。
双精度格式
按照IEEE-754标准来规定浮点数的存储格式,在C语言中,双精度浮点数用8字节存储,分为三个部分:符号位、阶和尾数。阶即指数,尾数即有效小数位数。符号位为1位,阶占11位,尾数占53位。编码规则为(也按照高字节在前,低字节在后顺序):
s【63】,e【53:62】,f【0:52】。
单精度浮点数实例
对于内部存储数据(四字节:0011 1111 0110 0110 0110 0110 0110 0110)2(2表示二进制,10表示十进制):
符号位:(最左侧)S=0。这表示是个正数
指数:(左侧第2-9位)E=(01111110)2=(126)10,所以e=E-127=-1。
尾数:(最后的23位)M=(1100110 01100110 01100110)2,m=(1.M)2 = (1.7999999523162841796875)10,或者m = (1.1100110 01100110 01100110)2
该二进制小数转为10进制的计算方式为1 + (1/2+1/4) + (1/32+1/64) + (1/512+1/1024)……
实际值:N=1.7999999523162841796875*2-1 = 0.89999997615814208984375,这个数据是0.9的单精度浮点数的实际内部存储,从中可以看到计算机对实际浮点数的存储是有一定的误差的。或者在N = (1.1100110 01100110 01100110)2 * 2-1的m值基础上进行移位操作,得到N = (0.11100110 01100110 01100110)2 = (0.89999997615814)10
二进制、十进制转换问题
十进制小数转换为二进制:小数乘以2取整,余下小数部分继续乘以2取整……,最后这些被取整数顺序排列,就是小数所要的二进制表示形式,例如对0.917,就有如下过程:
0.917 x 2 = 1.834 取1
0.834 x 2 = 1.668 取1
0.668 x 2 = 1.336 取1
0.336 x 2 = 0.672 取0
0.672 x 2 = 1.344 取1
0.344 x 2 = 0.688 取0
0.688 x 2 = 1.376 取1
0.376 x 2 = 0.752 取0
0.752 x 2 = 1.504 取1
0.504 x 2 = 1.008 取1
0.008 x 2 = 0.016 取0
0.016 x 2 = 0.032 取0
0.032 x 2 = 0.064 取0
0.064 x 2 = 0.128 取0
0.128 x 2 = 0.256 取0
0.256 x 2 = 0.512 取0
0.512 x 2 = 1.024 取1
0.024 x 2 = 0.048 取0
0.048 x 2 = 0.096 取0
0.096 x 2 = 0.192 取0
0.192 x 2 = 0.384 取0
0.384 x 2 = 0.768 取0
0.768 x 2 = 1.536 取1
0.536 x 2 = 1.072 取1
于是就有:(0.917)10 = (0.11101010 11000000 10000011)2 = (1.1101010 11000000 10000011)2 单精度,共24位,单精度存储为4字节,分布如下所示:高字节在前,低字节在后
S【0】,e【01111110】,f【1101010 11000000 10000011】,于是就是:
0011 1111 0110 1010 1100 0000 1000 0011;
二进制小数转换为十进制 f0/2 + f1/4 + f2/8 + … + fi/2i+1 + … + ;fi为对应二进制数位数字,为1或者0;
于是对于单精度4字节二进制数:0011 1111 0110 1010 1100 0000 1000 0011,转换为10进制浮点数就是:
S【0】 = 0为正数;
E【01111110】 = (126 - 127)10 = -1;
F【1101010 11000000 10000011】;m=(1.M)2 = (1.83399999)10,实际值为:1.83399999 ×2-1 = 0.91699999;跟0.917还是有些差别,这个差别称之为转换误差,显然误差和转换的精度有关。