浮点误差
综述
由于计算机存储格式限制,浮点数是存在误差的,这一点在计算几何中尤为突出,以下来探讨这个问题.
浮点数的精度
占字节数 | 数值范围 | 十进制精度位数 | |
---|---|---|---|
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
所以在输出时,