引入

大家在写C语言的程序的时候,有没有发现关于浮点数的计算总会出现那么一点问题,导致你的程序WA呢!下面便是对于这个现象的具体解释~

我们先从运行以下小程序,观察一下0.1+0.2的结果

#include <stdio.h>
int main(void){
double a,b;
scanf("%lf %lf",&a,&b);
printf("%.30f\n",a+b);
return 0;
}

运行结果
结果非常的amazing啊!0.1+0.2居然不等于0.3 这是为什么呢?
故事要从IEEE 二进制浮点数算术标准(IEEE 754讲起

浮点误差

在计算机中, 我们日常所使用的文档, 图片, 数字等, 在储存时, 实际上都要以二进制的形式存放在内存或硬盘中, 内存或硬盘就好像是一个被划分为许多小格子的容器, 其中每个小格子都只能盛放0或1...

![格子示意图](https://pic3.zhimg.com/80/v2-01aebebb171185b9d1eb1de7bca6d3ca_1440w.jpg

我们日常使用的 浮点数 也不例外, 最终也要被存储到这样的二进制小格子中.
这就出现了一个问题,我们要怎么存储浮点数呢?对于浮点数 20.5 , 是应该存储为 0100011 呢, 还是应该存储为 1100110 呢?

直到1985年, IEEE754标准问世, 浮点数的存储问题才有了一个通用的工业标准.

EEE-754标准规定了两种浮点数格式,分别是单精度浮点数双精度浮点数,具体如下:

单精度浮点数:32位二进制,有1位符号位、8位指数位和23位尾数位。最高位为符号位,0表示正数,1表示负数。接下来的8位指数位采用移码表示,可以表示正数、负数和零。23位尾数位包括整数部分和小数部分,用于保存有效数字和精度。(float)

双精度浮点数:64位二进制,有1位符号位、11位指数位和52位尾数位。最高位为符号位,0表示正数,1表示负数。接下来的11位指数位采用移码表示,可以表示正数、负数和零。52位尾数位、包括整数部分和小数部分,用于保存有效数字和精度。(double)

用上面的小格子,我们可以像这样显示出浮点数的表示方法浮点数的存储方法

类似于0.5、0.25、0.125这些数字 用这种办法表示非常的方便
但是!对于0.1 0.3这些数字,我们只能无限逼近,并不能做到完全相等
所以其对应的二进制数字就是一个无限小数
但是我们通过上面的标准可以知道——我们可以存储的位数是有限的,如果是单精度浮点型,就只有23位存储空间,如果是双精度,就只有52位存储空间,因此像这种无限循环下去的二进制数字,在实际存储的时候就会被截断。而这也就是误差的来源。对于一些需要精度的计算 如求 (1-a)(1-b)(1-c)(1-d)==某一浮点数 abcd∈(0,1) abcd可能的取值组数时
对于浮点数的验证便可能会出现问题
所以我们一直在尝试避免浮点误差!

浮点误差的避免

①不要直接比较浮点数

直接比较浮点数时,可能会因为浮点误差而得到错误的结果。就如 0.1+0.2 != 0.3
我们可以通过比较近似范围 相差不到一个很小的值就可以认为他们相等 可以避免浮点误差

②尽可能使用整数运算

在一些情况下,可以预先将浮点数转换为整数,进行乘以若干倍数的运算,然后再除以对应的倍数,这样可以避免浮点误差。

③使用高精度算法

如果精度要求很高,可以使用高精度算法,例如可以使用自带高精度计算功能的编程语言,或者第三方高精度计算库等。
例如Python的特殊数据类型deciaml

④设置适当的浮点数精度

有时可以使用四舍五入、向零取整等方式设置适当的精度来避免浮点误差。例如round函数,可以向最近的整数取整,也可以设置精度取小数点后几位

⑤避免迭代计算

迭代计算往往会引入严重的误差,因此应该尽可能避免使用。可以使用单次计算得到精确结果的方法来避免迭代计算。

Reference

[1] 百度百科-IEEE 754 https://baike.baidu.com/item/IEEE%20754/3869922?fr=ge_ala
[2] 【知识点随笔分享 | 第一篇】避不开的浮点误差 https://blog.csdn.net/fckbb/article/details/131347402
[3] 知乎-浮点数误差 https://zhuanlan.zhihu.com/p/133957594?utm_id=0
[4] 浮点数的误差 https://blog.csdn.net/cluster1893/article/details/80757724
[5] 知乎-为什么有浮点误差? https://www.zhihu.com/question/380574329/answer/1213162194

Last modification:September 22nd, 2023 at 08:06 pm
如果觉得我的文章对你有用,请随意赞赏 |´・ω・)ノ