浮点误差处理
为什么会出现浮点误差
所有的整数和小数都是通过二进制储存在电脑中的。在存较大整数的时候,例如存储 12 345 678 901 234 567 890时,我们存储时会因为有效数字的问题丢失一些有效数字,然而对0.6这种小数来讲,我们会发现在储存这种小数时因为有效数位的限制(对于binary64,通常是53位),我们发现这个0.6这个数需要舍弃数位。对于这种情况,根据IEEE提供RoundTiesToEven方法1,我们经常会发现0.6转换成BIN再转换成DEC经常会小数点后遥远的数位多一个1或少一个1.
如何处理浮点误差
对于判断两个浮点数的是否相等与大小,我们可以定义一个三出口的函数2
int sgn(double a){
return a<-eps?-1:a<eps?0:1;
}
eps是ε的英文读音简写。eps表示的是一个较小的量,它要被确保远大于浮点数的不确定量。我们通常取为1E-8 。
对于任何两个需要比较的浮点数,只需要将左值与右值作差并当作参数传入sgn() 函数中,将返回值与0进行欲进行的比较即可。我们简单地举个例子。
double a=0.6,b=0.4;
//This is the usual method
bool re1=(a<=b)
//This is the improved method
bool re2=(sgn(a-b)<=0);
结构体重载运算符
为什么要重载运算符
举一个最简单的例子。我们现在在priority_queue< Pivots > pvts里储存了很多个名为Pivots的结构体。优先队列里的比较大小必定是使用了>和<进行排序,但是这些运算符如果直接应用在结构体上必定会报错,因为运算符不知道如何比较两个结构体的大小。这时就需要我们对于这个结构体进行运算符重载。
重载运算符的规则
-
只有五个运算符不可以重载,他们分别是
- 成员访问运算符(.)
- 成员指针访问运算符(.*)
- 域运算符(::)
- 长度运算符(sizeof)
- 条件运算符(?)
-
不允许新定义运算符。
-
不允许改变运算符参数个数。
-
不允许给运算符添加默认的参数
重载后运算符的性质
-
重载后运算符的优先级不改变。
-
重载后运算符的结合性不改变。
-
重载后的运算符重载的内容只对用户自定义的类型和对象有效。
重载运算符的方法
首先我们先来定义一个结构体Edge
struct Edge{
int s;
int v;
int weight;
//bookmark 1#
}
然后为了举例,我们来重载运算符+和<
//The following snippet is to be inserted into the place of bookmark #1
Edge operator+(const Edge &other){
Edge newE;
newE.s=s+other.s;
newE.v=v+othe.v;
newE.weight=weight+other.weight;
return newE;
}
bool operator<(const Edge &other){
return cost<other.other;
}
http://babbage.cs.qc.cuny.edu/IEEE-754/ 和http://babbage.cs.qc.cuny.edu/IEEE-754.old/Decimal.html 是两个有用的网站,它们可以演示IEEE在Round方法方面上采取的办法执行出来的效果。感谢优快云用户h124668269的整理。 ↩︎
https://wenku.baidu.com/view/b6b7ccea551810a6f52486bf### 这个文档介绍了许多关于计算精度的问题。感谢优快云用户yiqzq的整理! ↩︎