2017-08-27 49 views
0

這是一個後續的問題我剛纔一個asked yesterdayDLL浮點結果根據呼叫者

的問題,即有超過4000行的代碼MSVS 2008年C++ DLL中發生的不同,但我已成功地生產一個簡單的例子,演示了我的CPU(AMD Phenom II X6 1050T)發生的問題。

它會顯示在另一個系統上發生的問題?我真的很想知道!

下面是一個簡單的類(Point.cpp),它需要被編譯爲DLL:

#include <math.h> 
#define EXPORT extern "C" __declspec(dllexport) 
namespace Test { 
    struct Point { 
     double x; 
     double y; 
     /* Constructor for a Point object  */ 
     Point(double xx, double yy) : x(xx), y(yy) {} 
     /* Copy constructor     */ 
     Point(const Point &rhs) : x(rhs.x), y(rhs.y) {} 
     double mag() const; 
     Point norm() const; 
     }; 
    double Point::mag() const {return sqrt(x*x + y*y);} 
    Point Point::norm() const { 
     double m = mag(); 
     return Point(x/m, y/m); 
     } 
EXPORT void __stdcall GetNorm(double x, double y, double *nx, double *ny) 
    Point P = Point(x, y); 
    Point N = P.norm(); 
    *nx  = N.x; 
    *ny  = N.y; 
    } 
} 

下面是測試程序(TestPoint.c),其需要被鏈接到lib爲DLL創建:

#include <stdio.h> 
#define IMPORT extern __declspec(dllimport) 
IMPORT void __stdcall GetNorm(double x, double y, double *nx, double *ny); 
void dhex(double x) { // double to hex 
    union { 
     unsigned long n[2]; 
     double d; 
     } value; 
    value.d = x; 
    printf("(0x%0x%0x)\n", value.n[1], value.n[0]); 
    } 
double i64tod(unsigned long long n) { // hex to double 
    double *DP = (double *) &n; 
    return *DP; 
    } 
int main(int argc, char **argv) { 
    double vx, vy; 
    double ux, uy; 
    vx = i64tod(0xbfc7a30f3a53d351); 
    vy = i64tod(0xc01b578b34e3ce1d); 
    GetNorm(vx, vy, &ux, &uy); 
    printf(" vx = %20.18f ", vx); dhex(vx); 
    printf(" vy = %20.18f ", vy); dhex(vy); 
    printf("\n"); 
    printf(" ux = %20.18f ", ux); dhex(ux); 
    printf(" uy = %20.18f ", uy); dhex(uy); 
    return 0; 
    } 

在我的系統,以測試點與VC++編譯,輸出爲:

vx = -0.18466368053455054 (0xbfc7a30f3a53d351) 
vy = -6.8354919685403077 (0xc01b578b34e3ce1d) 
ux = -0.027005566159023012 (0xbf9ba758ddda1454, 
uy = -0.99963528318903927 (0xbfeffd032227301b) 

但是,如果使用gcc編譯相同的代碼,或者確實如此,似乎任何等效程序(例如VB6,PowerBasic),結果(ux和uy)都微妙但絕對不同(最後一個十六進制數字):

vx = -0.184663680534550540 (0xbfc7a30f3a53d351) 
vy = -6.835491968540307700 (0xc01b578b34e3ce1d) 
ux = -0.027005566159023008 (0xbf9ba758ddda1453) 
uy = -0.999635283189039160 (0xbfeffd032227301a) 

這可能看起來並不重要,但是當它出現在物理引擎中時,這些差異以驚人的方式積累起來。 。

如果引擎根據誰來調用它會得到不同的結果,我可能不得不放棄使用VC++,而是嘗試使用g ++來代替。

+0

嗯,這不只是我的系統。我在我女兒的筆記本電腦上運行測試程序(英特爾CPU),同樣的差異顯而易見。我還使用g ++構建了一個我的引擎DLL版本,並且根據調用程序的不同,它也表現出相同的發散行爲。這讓我瘋狂! –

回答

1

好吧,我想我知道這是怎麼發生的。看看Point.dll的反彙編列表,我注意到GetNorm函數幾乎與你期望的一樣,有一些FMUL和FDIV。什麼是而不是目前是FLDCW指令。

在MSVC調用程序中也沒有任何FLDCW,但我在調用程序的gcc和PowerBasic版本中都找到了FLDCW。

所以我調整了其中一個可執行文件(PowerBasic EXE是最容易找到合適的地方進行調整的),然後嘿presto,然後我得到了匹配MSVC的答案。據推測,FLDCW已經改變了FPU舍入模式,因此最不重要的位有所不同。

+0

爲了讓我的引擎DLL爲任何調用者提供相同的結果,似乎我必須確保我對每個相關的DLL函數(幸運的是隻有一小部分)有相同的舍入模式,可能是通過執行特定的FLDCW指令。 –