2014-01-13 30 views
2

我一直在試圖單元測試我爲大地測量變換編寫的一個C++類。變量分組在優化代碼中提供不同的答案

我注意到三個變量的微小分組變化極大地影響了函數中的錯誤。

EDIT:下面是可編譯例如整個函數:

假設latitudelongitudealtitude是零。 Earth::a = 6378137Earth::b = 6356752.3我正在努力獲得基準數字,今天在工作中出現了一些問題,我不得不這樣做。

void Geodesy::Geocentric2EFG(double latitude, double longitude, double altitude, double *E, double *F, double *G) { 
    double a2 = pow<double>(Earth::a, 2); 
    double b2 = pow<double>(Earth::b, 2); 
    double radius = sqrt((a2 * b2)/(a2 * pow<double>(sin(latitude), 2) + b2 * pow<double>(cos(longitude), 2))); 
    radius += altitude; 

    *E = radius * (cos(latitude) * cos(longitude)); 
    *F = radius * (cos(latitude) * sin(longitude)); 
    *G = radius * sin(latitude); 

    return; 
} 

其中所有值被定義爲double包括那些在Earth

template <typename T> 
static inline T pow(const T &base, unsigned const exponent) { 
    return (exponent == 0) ? 1 : (base * pow(base, exponent - 1)); 
} 

有問題的代碼:

*E = radius * cos(latitude) * cos(longitude); 
*F = radius * cos(latitude) * sin(longitude); 

產生不同的結果:該pow<T>()函數是一個遞歸函數模板被定義

*E = radius * (cos(latitude) * cos(longitude)); 
*F = radius * (cos(latitude) * sin(longitude)); 

什麼是編譯器gcc做與優化級別3使這些結果1e-2有什麼不同?

+0

使用匯編程序,盧克。爲此嘗試gcc資源管理器。 (請注意,在這裏我沒有針對不同的版本獲得不同的彙編程序,但是ymmv) – PlasmaHH

+3

編譯不帶優化時會得到不同的結果嗎?如果半徑與sin/cos範圍[-1,1]非常大相同,那麼期望得到不同的結果 – egur

+0

讓我做一個基準,我會回到你們身上 –

回答

2

你有不同的舍入浮點不能代表所有的數字:

a * b * c;(a * b) * c可能比a * (b * c)不同。

您可能也有類似的問題與添加也。

例如用另外:

10e10f + 1.f == 10e10f

所以(1.f + 10e10f) - 10e10f == 10e10f - 10e10f == 0.f
1.f + (10e10f - 10e10f) == 1.f - 0.f == 1.f

+0

這並不解釋報道的行爲,除非1e-2差異的報告值很大(「半徑」必須很大)。在與正常值相乘時,對於一些舍入誤差e0和e1,浮點數(a * b)* c'恰好產生一個•b•(1 + e0)•c•(1 + e1) 2 ULP(以圓到最近的模式),而a *(b * c)'產生一個•(b•c•(1 + e2))•(1 + e3)對於某些誤差e2和E3。這兩者之間的最大差異出現在e0和e1爲+1/2 ULP且e2和e3爲-1/2 ULP時,除非「半徑」很大,否則差異將不是1e-2。 –

+0

如果'radius'是以米爲單位的地球半徑並且代碼使用32位浮點,則情況可能如此。 –

+2

隨着問題的更新,我們看到'radius'爲6378137,正在使用'double'。有了這些,'(a * b)* c'和'a *(b * c)'之間的差別並不能解釋觀察到的1e-2的誤差。 –

相關問題