引入
大家在写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
4 comments
这篇文章写得深入浅出,让我这个小白也看懂了!
你好,看完你的博客文章,感觉很不错!希望与你网站首页友情链接
大流量卡
http://53go.cn
专注于移动/联通/电信推出的大流量多语音活动长短期套餐手机卡的相关知识的介绍普及
听说互换友情链接可以增加网站的收录量,特此来换,如果同意的话就给internetyewu@163.com发信息或者就在此回复下吧!
通透了 感谢博主分享
谢谢吸吸吸!!!♪(・ω・)ノ