DLL浮点结果因调用者而异

时间:2017-08-27 01:46:18

标签: visual-c++

这是我之前的asked yesterday

的后续问题

问题出现在MSVS 2008 C ++ DLL中,该DLL有超过4000行代码,但我设法生成了一个简单的案例,演示了我的CPU(AMD Phenom II X6 1050T)上出现的问题。 / p>

它是否会显示另一个系统上发生的问题?我真的很想知道!

这是一个简单的类(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),需要链接到为DLL创建的lib:

#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 ++编译TestPoint,输出为:

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 ++

1 个答案:

答案 0 :(得分:1)

好的,我想我知道这是怎么发生的。看一下Point.dll的反汇编列表,我注意到GetNorm函数几乎是你所期望的,有几个FMUL和FDIV。 的内容是FLDCW指令。

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

所以我调整了一个可执行文件(PowerBasic EXE最容易找到合适的地方进行调整),嘿,我接下来得到了与MSVC相匹配的答案。据推测,FLDCW改变了FPU舍入模式,因此最低有效位的差异。