通常我们比较两个数据是否相等,可以使用如下表达式:
if (v1 == v2) or if (v1 != v2)
但是在实际项目中,不能使用这样的绝对相等表达式。比如1和0.9998实际上也是可以认为是相等的;又比如,设置某个电压参数,set_voltage = 100,000V,100KV后,通过传感器读取实际电压值:read_voltage = 100,005V 或 99,998V,然而这样的误差实际上是被允许的误差,由于硬件的物理特性,不可能真正做到100%的目标电压,只能尽可能的接近目标电压。为此在比较数据,尤其是浮点数据时,通常需要自定义浮点比较函数。
通用的浮点比较函数:
bool equal(double v1, double v2, double precision)
{
double unprecision = (precision < 0) ? (-1)*precision : precision;
double dt = ((v1 < v2) ? v2 : v1) - ((v1 < v2) ? v1 : v2);
return (dt > unprecision) ? false : true;
}
bool greater(double v1, double v2, double precision)
{
if (equal(v1, v2, precision)) {
return false;
}
else {
return (v1 > v2) ? true : false;
}
}
bool less(double v1, double v2, double precision)
{
if (equal(v1, v2, precision)) {
return false;
}
else {
return (v1 < v2) ? true : false;
}
}
传递一个precision用来控制精度,equal是主要的比较函数,greater和less是在equal的基础上进行比较。
double dt = ((v1 < v2) ? v2 : v1) - ((v1 < v2) ? v1 : v2);也可以用double dt = fabs(v1-v2);这里为了减少函数的调用,直接使用了代码;也可以声明为inline函数。
测试代码:
//循环测试打印
void equal_print(double base, double precision, const double* dt, int size,
const char* true_flag, const char* false_flag, function<bool(double,double,double)> func)
{
for (auto i=0; i<size; i++) {
if (func(base, base+*(dt+i), precision)) {
printf("%f %s %f, precision = %f\n", base, true_flag, base+dt[i], precision);
}
else {
printf("%f %s %f, precision = %f\n", base, false_flag, base+dt[i], precision);
}
}
}
//单元测试
void equal_unit_test()
{
//超大数据200000正负10以内
double precision = 10;
double baseVal = 200000;
double dt1[] = {0.0,0.1,-0.1,0.5,-0.5,1,-1,2,-2,5,-5,10,-10,10.1,-10.1,12,-12};
cout << "Test : 200000+10 >= x >= 200000-10" << endl;
equal_print(baseVal, precision, &dt1[0], sizeof(dt1)/sizeof(double), "==", "!=", &xpod_common::equal);
cout << endl << endl;
//高精度数据1正负0.001以内
precision = 0.001;
baseVal = 1.0;
double dt2[] = {0.0,0.0001,-0.0001,0.0005,-0.0005,0.001,-0.001,0.00101,-0.00101,0.0011,-0.0011,0.002,-0.002};
cout << "Test : 1+0.001 >= x >= 1-0.001" << endl;
equal_print(baseVal, precision, &dt2[0], sizeof(dt2)/sizeof(double), "==", "!=", &xpod_common::equal);
//大于比较
precision = 0.1;
baseVal = 100.0;
double dt3[] = {-1,-0.2,-0.11,-0.10,-0.05,-0.01,0.0,0.01,0.05,0.1,0.5,1,2};
cout << "Test : Greator" << endl;
equal_print(baseVal, precision, &dt3[0], sizeof(dt3)/sizeof(double), ">", "<>", &xpod_common::greater);
cout << endl << endl;
//小于比较
cout << "Test : Less" << endl;
equal_print(baseVal, precision, &dt3[0], sizeof(dt3)/sizeof(double), "<", "<>", &xpod_common::less);
}
int main(int argc, char *argv[])
{
//test equal
equal_unit_test();
return 0;
}
测试结果:
Test : 1+0.001 >= x >= 1-0.001
1.000000 == 1.000000, precision = 0.001000
1.000000 == 1.000100, precision = 0.001000
1.000000 == 0.999900, precision = 0.001000
1.000000 == 1.000500, precision = 0.001000
1.000000 == 0.999500, precision = 0.001000
1.000000 == 1.001000, precision = 0.001000
1.000000 != 0.999000, precision = 0.001000
1.000000 != 1.001010, precision = 0.001000
1.000000 != 0.998990, precision = 0.001000
1.000000 != 1.001100, precision = 0.001000
1.000000 != 0.998900, precision = 0.001000
1.000000 != 1.002000, precision = 0.001000
1.000000 != 0.998000, precision = 0.001000
Test : Greator
100.000000 > 99.000000, precision = 0.100000
100.000000 > 99.800000, precision = 0.100000
100.000000 > 99.890000, precision = 0.100000
100.000000 <> 99.900000, precision = 0.100000
100.000000 <> 99.950000, precision = 0.100000
100.000000 <> 99.990000, precision = 0.100000
100.000000 <> 100.000000, precision = 0.100000
100.000000 <> 100.010000, precision = 0.100000
100.000000 <> 100.050000, precision = 0.100000
100.000000 <> 100.100000, precision = 0.100000
100.000000 <> 100.500000, precision = 0.100000
100.000000 <> 101.000000, precision = 0.100000
100.000000 <> 102.000000, precision = 0.100000
Test : Less
100.000000 <> 99.000000, precision = 0.100000
100.000000 <> 99.800000, precision = 0.100000
100.000000 <> 99.890000, precision = 0.100000
100.000000 <> 99.900000, precision = 0.100000
100.000000 <> 99.950000, precision = 0.100000
100.000000 <> 99.990000, precision = 0.100000
100.000000 <> 100.000000, precision = 0.100000
100.000000 <> 100.010000, precision = 0.100000
100.000000 <> 100.050000, precision = 0.100000
100.000000 <> 100.100000, precision = 0.100000
100.000000 < 100.500000, precision = 0.100000
100.000000 < 101.000000, precision = 0.100000
100.000000 < 102.000000, precision = 0.100000
输出结果第8行有错误,对应第7行,也应该是相等的才对。这是因为计算机在表示浮点数时,不是完整的精确值,其有一定的误差,比如计算机可能用0.9998来表示1,因此当精度要求很高时,可能会带来偏差。为解决这个问题,在工程计算上一般会将高精度浮点数升级为整数后再计算精度。