浮点数的表示和精度
如果是a0的话,1 a一定比1大吗? 在数学上,答案是肯定的。 但是在计算机中,答案与a的大小和浮点数的精度有关。 在matalb中,可以执行以下计算:
a=1/2^52
a=
2.220446049250313e-016
1 a1
ans=
1
a=1/2^53
a=
1.110223024625157e-016
1 a1
ans=
0
可知a为1/2^53时,1 a1不成立。
1浮点数
IEEE754定义了单精度浮点数和双精度浮点数:浮点和双精度浮点数。 浮点为32位,双精度为64位。 这些包括符号位、指数和尾数。
符号位
指数
尾数
浮动
三十一(一) )。
30-23(8)
22-0(23 )
双精度
63 (一) )。
62-52(11 )
51-0(52 )
符号有1bit,0表示正,1表示负。 设某个数的指数为e,指数部分的值为bias e。 添加了bias以表示负数。 浮点的bias为127,双精度的bias为1023。 指数对全0或全1有特殊意义,不是正常指数。
浮点的指数部分有8bit,取值1~254,减去127,得到对应的指数范围-126~127。
double的指数部分有11位,取值1~2046,减去1023,可以得到对应的指数范围-1022~1023。
这里的指数以2为底,同样,尾数也是二进制。 在IEEE754中,必须以正规格式存储小数点前有一位非零数字的浮点数。 对于二进制数,非零数字只有1。 所以IEEE754省略这个小数点前的1并记忆,只记忆小数点后的位数。
2误差
让我们看一个例子。
double a=0.2;
在PC中,与a对应的bank数据如下所示。
9A 99 99 99 99 99 C9 3F
如果将PC的数据末尾小,即低位字节位于后面,高位字节位于前面,则如下所示。
3F C9 99 99 99 99 99 9A
可见符号位是0。 指数为0x3FC,即1020,减去1023得到指数-3。 尾数为999999999999A。 所以完整的数字是十六进制的1.999999999999A乘以2^-3。 也就是说:
a=(19* )1/161/16^2.1/16^12 ) 10/16^13 ) *2^-3
(1/16 … 1/16^12 )可以用等比级数和公式a1*(1-q^n )/(1- q )进行计算。 其中a1=1/16,q=1/16,n=12,所以:
a=(19*(1-1/16^12 )/15 10/16^13 ) *2^-3
使用windows的计算机计算得到上式
a=0.2000000000001110223024625157
这也不是严格解,但可以看到用double表示0.2时存在的误差。 这个例子表示,用有限字长的2进制浮点数表示任意实数a的话,有可能会导入误差。 假设实数a的指数为e,尾数位数为n,则很明显:
误差
3精度
可以将机器的精度定义为满足条件
fl(1) 1
的最小浮点数。 这里,fl(1 )是1的浮点表示。 很明显,double的机械精度是1/2^52。 float的机械精度为1/2^23。 matlab内部采用双精度,1 1/2^53对双精度来说是1,所以1 1/2^53不会大于1。
对于正规数,浮点的有效数字为24位,对应于8位十进制有效数字,因为小数点前缺省为1。 双精度有效数字为53位,与16位十进制有效数字相对应。
4特殊浮点数
上面提到的浮点数指数对所有0或所有1都有特殊的意义,所以让我们来看看这些特殊的浮点数:
指数和尾数都用0表示0。 根据符号比特可以分为0和-0。
指数全0,尾数不全0这个数是非正规数,尾数部分不假定前面有小数点前的1。 或者这些数太接近0,指数不能再小了,所以这些数不能写成规范形式。 例如,双精度数0000 0000 0000 0001的尾数为0 0000 0000 0001,即1/2^52,相应的数为1/(2^-1022,即4.9406564584124654e-324
指数全1,尾数全0表示无限大,即inf。 可以根据符号比特分为inf和-inf。
指数全1,尾数不全0表示NaN,即Not a Number,不是数字。 尾数的最高位为1的NaN称为qnan(quietnan )。 尾数最高位
为0的NaN被称作SNaN(Signalling NaN)。通常用QNaN表示不确定的操作,用SNaN表示无效的操作。在计算机内部,double就是一个64位数。从0x0000 0000 0000 0000~0xFFFF FFFF FFFF FFFF,每个64位数都对应一个浮点数或NaN。 我写了一个小程序,按照64位无符号整数的顺序打印出典型的浮点数。 表格的第一列是浮点数的内部表示。为了便于阅读,按大尾顺序输出。第二列是对应的浮点数。 第三列是注释,对于非规范数和规范数给出了由内部表示计算数值的matlab算式。 注意在C/C++中,2^52要写成pow(2.0,52.0)。
0000 0000 0000 0000
0.0000000000000000e+000
+0
0000 0000 0000 0001
4.9406564584124654e-324
1/(2^52)*2^-1022
000F FFFF FFFF FFFF
2.2250738585072009e-308
.5*(1-.5^52)/(1-.5)*2^-1022
0010 0000 0000 0000
2.2250738585072014e-308
1.0*2^-1022
0010 0000 0000 0001
2.2250738585072019e-308
(1+1/2^52)*2^(-1022)
001F FFFF FFFF FFFF
4.4501477170144023e-308
(1+.5*(1-.5^52)/(1-.5))*2^-1022
0020 0000 0000 0000
4.4501477170144028e-308
1.0*2^-1021
3FF0 0000 0000 0000
1.0000000000000000e+000
1.0
3FF0 0000 0000 0001
1.0000000000000002e+000
1.0+1/(2^52)
3FFF FFFF FFFF FFFF
1.9999999999999998e+000
1+.5*(1-.5^52)/(1-.5)
4000 0000 0000 0000
2.0000000000000000e+000
1.0*2^1
7FEF FFFF FFFF FFFF
1.7976931348623157e+308
(1+.5*(1-.5^52)/(1-.5))*2^1023
7FF0 0000 0000 0000
1.#INF000000000000e+000
+INF
7FF0 0000 0000 0001
1.#SNAN00000000000e+000
SNaN
7FF7 FFFF FFFF FFFF
1.#SNAN00000000000e+000
SNaN
7FF8 0000 0000 0000
1.#QNAN00000000000e+000
QNaN
7FFF FFFF FFFF FFFF
1.#QNAN00000000000e+000
QNaN
8000 0000 0000 0000
0.0000000000000000e+000
-0
8000 0000 0000 0001
-4.9406564584124654e-324
-(1/(2^52)*2^-1022)
800F FFFF FFFF FFFF
-2.2250738585072009e-308
-(.5*(1-.5^52)/(1-.5)*2^-1022)
8010 0000 0000 0000
-2.2250738585072014e-308
-(1.0*2^-1022)
8010 0000 0000 0001
-2.2250738585072019e-308
-((1+1/2^52)*2^(-1022))
801F FFFF FFFF FFFF
-4.4501477170144023e-308
-((1+.5*(1-.5^52)/(1-.5))*2^-1022)
8020 0000 0000 0000
-4.4501477170144028e-308
-(1.0*2^-1021)
BFF0 0000 0000 0000
-1.0000000000000000e+000
-1.0
BFFF FFFF FFFF FFFF
-1.9999999999999998e+000
-(1+.5*(1-.5^52)/(1-.5))
C000 0000 0000 0000
-2.0000000000000000e+000
-(1.0*2^1)
FFEF FFFF FFFF FFFF
-1.7976931348623157e+308
-((1+.5*(1-.5^52)/(1-.5))*2^1023)
FFF0 0000 0000 0000
-1.#INF000000000000e+000
-INF
FFF0 0000 0000 0001
-1.#SNAN00000000000e+000
SNaN
FFF7 FFFF FFFF FFFF
-1.#SNAN00000000000e+000
SNaN
FFF8 0000 0000 0000
-1.#IND000000000000e+000
QNaN
FFFF FFFF FFFF FFFF
-1.#QNAN00000000000e+000
QNaN
从表中可以看到,double内部表示的设计是很有规律的,按照对应64位数的顺序依次为 +0、正非规范数、正规范数、正无穷大、符号位为正的NaN、-0、负非规范数、负规范数、负无穷大、符号位为负的NaN。
double内部表示的设计保持了浮点数的有序性。即:如果正double数a
4 结束语
float和int都是32bit,但float的尾数只用了23bit。int的精度高于float,float的表示范围大于int。float牺牲精度换取了更大的表示范围。 double的尾数是52bit,高于32bit的int,所以用dobule表示int不会有精度损失。 double是科学计算的常用类型,了解double的内在和限制,有助于我们更好地使用它。
喜欢 (1)or分享 (0)