科学计数法和IEEE标准
在计算机中,是用二进制的科学计数法来表示和存储浮点数的。因为科学计数法可以唯一地表示任何一个数,且所占用的存储空间会更少。
比如:对于一个二进制数
100000...000(共127个0)
,如果不用科学计数法,需要16个字节来存储。如果用科学计数法:1*2^127,只需要用二进制表示出:有效数字和指数即可,压根不需要16个字节。
IEEE浮点标准用V=(-1)^s * M * 2^E
的形式来表示一个数:
- 符号:s决定该数是正数还是负数(0正1负),而对于数值0的符号位解释作为特殊情况处理。
- 尾数(相当于有效数字):M是一个二进制小数,对于规格化表示:1<=M<2
- 阶码(相当于指数):阶码E决定了二进制小数的小数点位置。(可为负数)
将一个浮点数的表示转成如上形式,然后分别对符号、尾数和阶码进行编码就能得到浮点数的机器表示。
如下,IEEE标准规定:
- 对于单精度浮点数,1位符号位+8位阶码位+23位尾数位。
- 对于双精度浮点数,1位符号位+11位阶码位+52位尾数位。
IEEE标准规定:阶码位表示的是无符号数e,阶码E
和无符号数e
的关系是:E = e - (2^(n-1) - 1)
。
比如,对于单精度浮点数(8位阶码位),无符号数e的范围是[0, 255],因此E = e - (2^7 - 1) = e - 127
,所以阶码E的范围是[-127, 128],即指数的范围是[-127, 128]。
尾数的规格化表示: 尾数M必须1<=M<2
。
为什么要规格化?
保证浮点数有唯一的表示。若不对浮点数的表示作出明确规定,同一个浮点数的表示就不是唯一的,比如对于十进制数1.75表示可能有1.11*2^0、0.111*2^1、0.0111*2^2等。
规格化表示后,尾数一定是1.xxxx
,由于第一位一定是1,所以不需要显式地表示它,因此尾数位全部用来表示尾数1.xxxx
之后的xxxx
部分,也就是说【尾数的长度(去除小数点)】最多是【尾数位+1位】,尾数超过这个长度之后的数字位都会被舍弃,从而出现精度问题。
示例
将如下十进制数转成单精度浮点数表示:
- 1.5
- -12.5
对于1.5,其二进制小数是1.1
,按照IEEE标准转成V=(-1)^s * M * 2^E
的形式,即(-1)^0 * 1.1 * 2^0
,所以:s=0;M=1.1;E=0。然后分别用1位符号位、8位阶码位和23位尾数位进行编码:
- 因为是正数,所以单精度浮点数的1位符号位为
0
- 尾数是
1.1
,尾数位只需存储1.1
之后的1
,因此单精度浮点数的23位尾数位就是10000000000000000000000
- 阶码E=0,则
e=E+127=127
,因此阶码位对应的无符号数e是127
,所以单精度浮点数的8位阶码位是01111111
最终,二进制表示为00111111110000000000000000000000
同理,对于-12.5,其二进制小数是-1100.1
,即(-1)^1 * 1.1001 * 2^3
,所以:s=0;M=1.1001;E=3。然后分别用1位符号位、8位阶码位和23位尾数位进行编码:
- 因为是负数,所以符号位是
1
- 尾数是
1.1001
,尾数位只需存储1.1001
之后的1001
,所以尾数位是10010000000000000000000
- 阶码E=3,则
e=E+127=130
,因此阶码位对应的无符号数e是130,则阶码位是10000010
最终,二进制表示为11000001010010000000000000000000
精度问题
单精度浮点数有23位尾数位,最多只能表示23+1=24
位长度的尾数;双精度浮点数有52位尾数位,最多只能表示52+1=53
位长度的尾数。
也就是说:
- 对于单精度浮点数,若尾数的长度超过24位,24位之后的数字就无法存储,会出现精度问题
- 对于双精度浮点数,若尾数的长度超过53位,53位之后的数字就无法存储,会出现精度问题