「CodeNote」 浮点误差

Posted by Dawn-K's Blog on August 6, 2019

浮点误差

参考博客-韬光养晦

综述

由于计算机存储格式限制,浮点数是存在误差的,这一点在计算几何中尤为突出,以下来探讨这个问题.

浮点数的精度

  占字节数 数值范围 十进制精度位数
float 4 -3.4e-38~3.4e38 6~7
double 8 -1.7e-308~1.7e308 14~15

对于同一个数,可能经由不同的计算方法得到,所以在低几位上可能不同,这个对”==”运算产生了致命的影响,因为浮点数只有完全一致才会返回true,为了解决这个问题,我们引入eps,来辅助判断浮点数相等的问题

sgn&eps

sgn函数常用于判断小数的符号,我们认为[-eps,eps]都算入0的范围内,因此定义如下:

int sgn(double a){return a < -eps ? -1 : a < eps ? 0 : 1;}

eps缩写自epsilon,表示一个小量,但这个小量又要确保远大于浮点运算结果的不确定量。eps最常见的取值是1e-8左右

传统意义 修正写法1 修正写法2
a == b sgn(a - b) == 0 fabs(a – b) < eps
a != b sgn(a - b) != 0 fabs(a – b) > eps
a < b sgn(a - b) < 0 a – b < -eps
a <= b sgn(a - b) <= 0 a – b < eps
a > b sgn(a - b) > 0 a – b > eps
a >= b sgn(a - b) >= 0 a – b > -eps

推荐使用第一种写法,第二种很容易因为eps的正负号出问题

换言之,修正写法1的本质是将a,b放到比较运算符的一侧,然后整体带入sgn函数中,来和另一侧的0比较大小.

输出问题

使用printf()输出小数时,会带来一个问题,就是四舍五入,比如

1
2
3
4
    double a = 0.005000000001;
    double b = 0.004999999999;
    printf("a = %.2lf b = %.2lf\n", a, b);
	// 得到的结果是 a = 0.01 ,b = 0.00

所以在输出时,