浮点数的范围和精度问题(float和double的比较)
今天遇到一道C++题,非常简单,就是比较两个浮点数是否相等。我的第一思路就是输入两个double类型的变量a,b
,看a-b
的绝对值是否小于
1
e
−
8
1e^{-8}
1e−8,是则相等。
但是!!!问题并没有到这里结束,我去瞄了一眼其他人的解法,发现居然可以输入float类型的变量,然后直接用逻辑判断==
就可以了,然后在线测评也通过了,也是非常神奇。于是对float和double类型的范围和精度产生了兴趣,为啥误差精度为
1
e
−
8
1e^{-8}
1e−8时float类型的数就能直接判断,而double却不行呢?
到网上搜罗到了一些解答,本文对其进行了归纳整理。
这首先得从浮点数在计算机中的表示和存储方式说起。
根据IEEE标准,浮点数是通过科学计数法来存储的,比如120.5用十进制的科学计数法来表示就是
1.205
×
1
0
2
1.205\times10^{2}
1.205×102,但是计算机中所有数据都是用二进制存储的,所以得先转换为二进制数,即
1.1110001
×
2
6
1.1110001\times2^{6}
1.1110001×26。
浮点数在计算机中的存储分为三个部分:
1. 符号位(sign):float和double符号位均为1位,0代表正数,1代表负数
2. 指数位(exponent):存储科学计数法中的指数部分,采用移位存储
3. 尾数位(fraction):存储科学计数法中的尾数部分
根据IEEE 754标准,单精度float类型使用32比特存储,其中1位表示符号,8位表示指数,23位表示尾数;双精度double类型使用64比特存储,1位符号位,11位指数位,52位尾数位。
然后再来讲讲移位存储。拿float来举例,内存中使用32位来存储float浮点数,其中第1位表示浮点数正负,第2~9位表示指数,后23位表示指数。表示指数的8位二进制虽然连续,但并不在同一字节中(实际上是由第一字节的后7位和第二字节的第1位组成),然后这8位指数位的第1位也是用来表示正负……然后我就没怎么弄懂了,欢迎大家赐教。总之如果按照移位存储,指数值最后需要加上偏移值127就对了。
###浮点数的范围
后来发现在头文件float.h里其实已经解释的很清楚了(所以说没事多翻翻源码总是没错的):
float能够表示的最大值为 3.40 × 1 0 38 3.40\times10^{38} 3.40×1038,最小区分精度约为 1.19 × 1 0 − 7 1.19\times10^{-7} 1.19×10−7(这一点和最开始那道题目有点矛盾,等我尝试几组数据之后再补充)。
double能够表示的最大值为 1.80 × 1 0 308 1.80\times10^{308} 1.80×10308,最小区分精度约为 2.22 × 1 0 − 16 2.22\times10^{-16} 2.22×10−16。
不过这宏定义的最大最小值感觉并不是我们所认为的“不溢出内存的情况下所能表示的最大数字”,我现在也说不清,留个问题之后慢慢琢磨好了。
浮点数的精度
这个解释起来轻松一点,精度这里指的是最大有效数字的位数,即只需要考虑尾数部分就可以啦。
对于float类型,尾数部分是23,转换成10进制的精度,
2
23
=
1
0
x
2^{23}=10^x
223=10x -->
x
=
23
l
o
g
2
≈
6.92
x=23log2\approx6.92
x=23log2≈6.92,所以23位2进制最多只能表示6位10进制数,这里就是头文件中FLT_DIG=6
的来由。
但还有一种说法,也是网上的主流说法,二进制小数点的第一位永远都是1,可以省略,实际上二进制的精度为24,所以10进制的精度为7。