关于浮点数的精度丢失问题的思考
在实际编程中,我遇到了一些蛮有意思的情况,就是浮点型变量初始化是不精确的,但这个不精确数却是固定不变的?另外,为什么
float
和double
的表示范围如此与众不同?
一 浮点数精度丢失的原因
浮点型表示单精度、双精度和扩展精度值。 C++ 标准指定了一个浮点数有效位数的最小值,然而大多数编译器都实现了更高的精度。 通常, float
以一个字(32比特)来表示,
double
以2个字(64比特)来表示,
long double
以3或4个字(96或128比特)来表示。
一般来说,类型 float
和 double
分别有7和16个有效位;类型 long double
则常常被用于有特殊浮点需求的硬件,它的具体实现不同,精度也各不相同。( 《C++ Primer第五版》 )
由以上描述,我们不难知道,对于 float
来说,最多只有7个有效位,这也就意味着,当实际存储的精度大于 float
的精度范围时,就会出现精度丢失现象。
二 精度丢失的深入提问
问题在于,所谓的有效位很让人迷惑,我明明占了那么多位置(float占4个字节double占8字节),为什么有效位只有6个?为什么整数位越多,精度越小?
三 浮点数精度问题的深入思考
1. 浮点数的表示
依据IEEE754二进制浮点数算术标准,一个浮点数可以表示为v = s * e * f
(即符号位乘以指数偏移值再乘以分数值)
2. 计算机对浮点数的处理步骤
- 将浮点数转化成二进制
- 用二进制科学计数法表示
- 表示成IEEE 754格式
其中在第一步和第三步中出现了精度丢失的情况
有了以上了解,我们就可以更好地理解浮点数的存储
3. 浮点数的存储
(1)存储步骤讲解
①第一步
②第二步
③第三步
④一些特殊规定
(2)指数偏移量
表示单精度浮点数时,是用8位去存储指数部分,即表示0-255,但我们同样需要有负指数,正负指数的位数量为了均等,各自一半,-127-128。0是特殊点,特殊处理。储存时候会加上127,这样就刚刚好是0~255,就能很好的储存了,不然,不移量的话需要判断符号位来判断数值的正负。
(3)实际演示
四 float与double的范围和精度
范围
简称的类型名 | 完整类型名 | 长度(字节) | 取值范围 |
---|---|---|---|
float | float | 4 | 绝对值:3.4* 10-38~3.4* 1038 |
double | double | 8 | 绝对值:1.7* 10-308~1.7* 10308 |
long double | long double | 8 | 绝对值:1.7* 10-308~1.7* 10308 |
float
的指数位有8位,而double
的指数位有11位,分布如下:
float
:
1bit(符号位) 8bits(指数位) 23bits(尾数位)
double
:
1bit(符号位) 11bits(指数位) 52bits(尾数位)
于是,float的指数范围为-127 ~ +128,而double的指数范围为-1023~+1024,并且指数位是按补码的形式来划分的。
float的范围为2^-128 ~ 2^128,也即3.40E-38 ~ +3.40E+38;double的范围为2^-1024 ~ +2^1024,也即1.79E-308 ~ +1.79E+308。
精度
float和double的精度是由尾数的位数来决定的。浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的“1”,由于它是不变的,故不能对精度造成影响。
float:2^23 = 8388608,一共七位,这意味着最多能有7位有效数字,但绝对能保证的为6位,也即float的精度为6~7位有效数字;
double:2^52 = 4503599627370496,一共16位,同理,double的精度为15~16位。
五 解决方法
通过网上查询资料,我得到一些方法:
①double
类型可以解决大部分精度丢失问题(虽然它也会不精确,但日常精确度够用)
②C++中cout
其实是可以解决这种精度丢失问题的,但是影响效率(毕竟多读好多位)
#include<iostream>
using namespace std;
int main(void)
{
float f_num1 = 21.75;
float f_num2 = 13.45;
cout << "f_num1 = " << f_num1 << endl;
cout << "f_num2 = " << f_num2 << endl;
cout << "f_num1 + f_num2 = " << f_num1 + f_num2 << endl;
return 0;
}
③浮点数转换成字符串类型,字符串存储时,不需要数据转换,也就不会出现精度丢失问题了。
④(不推荐,萌新发现,不知道坑在哪里)不在初始化的地方给值,偶现精度未丢失
这种方法肯定有问题,不然别人早发现了,另外在后期实验中,发现这种方法有时不起效果,仅仅作为一个小问题列出;