2017-02-09 39 views
0

我正在努力提高surf.cpp的表現。從線140,就可以找到此功能:爲什麼使用double,然後轉換爲float?

inline float calcHaarPattern(const int* origin, const SurfHF* f, int n) 
{ 
    double d = 0; 
    for(int k = 0; k < n; k++) 
     d += (origin[f[k].p0] + origin[f[k].p3] - origin[f[k].p1] - origin[f[k].p2])*f[k].w; 
    return (float)d; 
} 

運行Intel Advisor矢量分析,它表明「本1個數據類型轉換」,這可能是低效的(特別是在矢量化)。

但是我的問題是:看這個函數,爲什麼作者會創建d作爲double,然後把它投到float?如果他們想要一個十進制數,float就可以。我想到的唯一原因是,因爲doublefloat更精確,所以它可以代表較小的數字,但最終值足夠大,可以存儲在float中,但我沒有對d值執行任何測試。

任何其他可能的原因?

+1

也許'f [k] .w'是一個'double'。 –

+0

@ tobi303 ehm [nope](http://stackoverflow.com/questions/10108053/ranges-of-floating-point-datatype-in​​-c) – justHelloWorld

+0

@FrançoisAndrieux等等呢? :)你可以總結兩個雙打,並將結果保存在一個沒有任何投球的浮球,對吧? – justHelloWorld

回答

7

因爲作者希望在計算過程中具有更高的精度,那麼只能圍繞最終結果。這與在計算過程中保留更重要的數字相同。

更確切地說,當加法和減法時,可以累積誤差。當涉及大量浮點數時,這個錯誤可能相當嚴重。

+0

這很奇怪。爲什麼他們不會在乘以整數之前將'f [k] .w'強制轉換爲'double' **。這樣,代碼可以利用總和的更高精度,但決定不考慮加數。這真的很奇怪。 – IInspectable

+0

似乎只有從1到n的積累提升了一倍。內循環中,這4個數字保持在較低的分辨率... –

+0

@IInspectable可能是因爲它沒有太大的區別?看我的答案中的例子。 –

4

你質疑的答案是說在總和期間使用更高的精度,但我不明白爲什麼。答案是正確的。考慮一個完全虛構的人數這個簡化的版本:

#include <iostream> 
#include <iomanip> 

float w = 0.; 

float calcFloat(const int* origin, int n) 
{ 
    float d = 0; 
    for(int k = 0; k < n; k++) 
     d += origin[k] * w; 
    return (float)d; 
} 

float calcDouble(const int* origin, int n) 
{ 
    double d = 0; 
    for(int k = 0; k < n; k++) 
     d += origin[k] * w; 
    return (float)d; 
} 


int main() 
{ 
    int o[] = { 1111, 22222, 33333, 444444, 5555 }; 
    std::cout << std::setprecision(9) << calcFloat(o, 5) << '\n'; 
    std::cout << std::setprecision(9) << calcDouble(o, 5) << '\n'; 
} 

的結果是:

6254.77979 
6254.7793 

因此,即使輸入是在兩種情況下是相同的,你使用double了不同的結果中間求和。將calcDouble更改爲使用(double)w不會更改輸出

這表明(origin[f[k].p0] + origin[f[k].p3] - origin[f[k].p1] - origin[f[k].p2])*f[k].w的計算精度很高,但求和過程中的錯誤累積是他們試圖避免的。

這是因爲處理浮點數時錯誤如何傳播。引用The Floating-Point Guide: Error Propagation

一般:

  • 乘法和除法是「安全的」操作
  • 加減法是很危險的,因爲當不同幅度的數目都參與其中,較小幅度的數字號碼丟失了。

所以你想要更高精度類型的總和,其中涉及加法。將整數乘以double而不是float幾乎沒有多大關係:您將得到的結果與開始時的值(與結果不是非常大或非常非常相近)小)。但總結float值可能有非常不同的數量級,即使個別數字本身可表示爲float,也會累積錯誤並偏離真實答案。

地看到,在行動:

float f1 = 1e4, f2 = 1e-4; 
std::cout << (f1 + f2) << '\n'; 
std::cout << (double(f1) + f2) << '\n'; 

或等價,但更接近原始代碼:

float f1 = 1e4, f2 = 1e-4; 
float f = f1; 
f += f2; 
double d = f1; 
d += f2; 
std::cout << f << '\n'; 
std::cout << d << '\n'; 

結果是:

10000                                                    
10000.0001 

添加浮點數失去精確。即使輸入相同,將float添加到double也會給出正確的答案。您需要9位有效數字來表示正確的值,這對於float來說太多了。

+0

*「將'calcDouble'更改爲使用'(double)w'不會改變輸出。」* - 公平地說,它不會更改輸出,因爲您隨機選取的輸入**。這遠不是一個證明,我很抱歉。 – IInspectable

+1

我很抱歉,但你沒有要求提供證據,我也沒有聲稱要提供證明。如果你不明白爲什麼使用'double'來計算總和,你需要閱讀浮點數和錯誤傳播。我已經爲你添加了一個參考。 –

+0

@Jonathan Wakely很好的例子和解釋。 –

相關問題