概述
C语言里面支持两种浮点数类型:float和double,其中在32位机器上,float是32bit的变量类型,而double是双字也就是64bit的。编程的时候,有时需要知道两种数据数据表示范围和表示精度,下面给出两种结果的求解过程。
浮点格式
精度和范围与数据的存储格式密切相关,所以我们先来看一看它们的存储格式: 对于float类型的变量,其底层的存储格式为:
对于double类型的变量,底层的存储格式为:
浮点数底层的格式告诉我们它是以科学计数法表示浮点数据的,我们用S表示1bit的符号位,F表示23bit或52bit的尾数域,E表示8bit或者11bit的指数域,那么其科学表示法的的形式为:
(
?
1
)
S
?
F
?
2
E
(-1)^S*F*2^E
(?1)S?F?2E 为了尽可能地充分利用各个bit,根据科学计数法的格式,二进制的科学计数法小数点前是1时才是标准的写法,例如
0.101
?
2
2
、
10.101
?
2
2
0.101*2^2、10.101*2^2
0.101?22、10.101?22就不是标准的计数方式,
1.01
?
2
1
、
1.0101
?
2
3
1.01*2^1、1.0101*2^3
1.01?21、1.0101?23就是标准的计数格式,所以可以省略小数点前的1,这样就可以用尽可能多的bit表示尾数域,从而提高浮点数的精度。
按照IEEE标准浮点格式的类型表示了所有的实数域,包括三个特殊的数:0和正负无穷大。我们以float类型的浮点数为例,当所有bit都为0的时候,表示0.0f,它的32bit为:0000 0000 0000 0000 0000 0000 0000 0000。它表示的真实值为:
+
1.0
?
2
?
127
+1.0*2^{-127}
+1.0?2?127(是一个很接近于0的正数,这个时候也可以看做正的无穷小;当然符号位也可以是1,这个时候表示的是负无穷小,也就是-0),指数部分之所以是-127而不是0,是因为IEEE标准规定指数部分减去127才是才是真正的指数值,这里面有两个问题,我们一一解答:第一,为什么要减一下?首先,指数域的8bit表示的是无符号的8bit,因为浮点数包含了无穷小的数,所以指数部分肯定有为负数的情况,那为什么不直接用有符号的8bit表示?是因为为了方便在符号位一样的条件下可以直接通过指数域比较浮点数的大小,如果是sign 类型那么-1就表示为0xff,看起来是很大的一个数,所以需要减一下,才有可能出现指数域为负数的情况。第二,为什么是127而不是128或者别的值?只有减去127的时候,才能保证无穷大时的情况和无穷小时的情况的指数符号相反,保持平衡。无穷大的时候就是指指数域和尾数域全部是1的情况,当符号位为1、0时就可以分别代表负的无穷大和正的无穷大(实际上只是一个理论的无穷大,还是有范围的,下面会讲)。
范围推导
我们先详细的计算一遍float类型的范围,然后double类型的直接给出结果。首先看它的最大的情况,此时S=0,F全部是0,指数域全部是1,此时32bit的值为:0111 1111 1000 0000 0000 0000 0000 0000。它的精确值为:
+
1.
0
2
?
2
255
?
127
+1.0_2*2^{255-127}
+1.02??2255?127,对应的十进制约为:
+
1.0
?
2
+
128
≈
+
3.4
?
1
0
38
+1.0*2^{+128} \approx{+3.4*10^{38}}
+1.0?2+128≈+3.4?1038,负的无穷大就是当S=1的时候,即:
?
1.0
?
2
+
128
≈
?
3.4
?
1
0
38
-1.0*2^{+128} \approx{-3.4*10^{38}}
?1.0?2+128≈?3.4?1038,正无穷小时的实际值上面已经给出,这里不再赘述。另外在IEEE标准中将E全是1,F中非零表示NaN(Not a Number),它表示运算得到的结果不是一个数,比如1/0(除数为0)的结果就是NaN。
直接给出double类型的数表示的范围:
?
1.7
?
1
0
+
308
~
+
1.7
?
1
0
+
308
-1.7*10^{+308} \sim +1.7*10^{+308}
?1.7?10+308~+1.7?10+308。
精度计算
精度是和尾数域直接相关的,所谓的单精度双精度,它们的其中一个差别就在与后者的尾数域所占的bit比前者多很多。
我们可以这样理解:精度的“颗粒”就是尾数域的最后一个bit每增加1,我们的实际值(十进制下)会增加多少?仍然以float类型为例,我们知道float变量的尾数域长度为23bit,也就是小数点后有23个二进制bit,那么每增加1,对应的十进制就增加了
2
?
23
≈
0.00000011
9
10
2^{-23}\approx{0.000000119_{10}}
2?23≈0.00000011910?。也就是说,float类型的精度在十进制里面小数点后6位一定时有效的,第七位可能有效。同理double类型的十进制下的精度为15~16位,第15位一定有效。
总结
类型 | 有效位 | 范围 |
---|
float | 6~7 |
?
3.4
?
1
0
38
+
3.4
?
1
0
38
-3.4*10^{38}+3.4*10^{38}
?3.4?1038+3.4?1038 | double | 15~16 |
?
1.7
?
1
0
+
308
~
+
1.7
?
1
0
+
308
-1.7*10^{+308} \sim +1.7*10^{+308}
?1.7?10+308~+1.7?10+308 |
|