C++通用浮點數比較

一般咱們比較兩個數據是否相等,可使用以下表達式:less

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函數。大數據

測試代碼:code

//循環測試打印
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;
}

測試結果:ci

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,所以當精度要求很高時,可能會帶來誤差。爲解決這個問題,在工程計算上通常會將高精度浮點數升級爲整數後再計算精度。it

相關文章
相關標籤/搜索