C vs C ++(CLI)中调试器中的浮点表示

时间:2017-11-01 01:32:13

标签: c++ c debugging floating-point c++-cli

一点背景:我正在使用C#中间层进行从C++/CLIfloats的一些数据转换,我注意到了它的特殊性调试器显示doublesC#的方式,具体取决于代码执行的dll(请参阅下面的代码和图像)。起初我认为它与托管/非托管差异有关,但后来我意识到如果我完全离开dllimport层并且只使用非托管数据类型,则表现出相同的行为。

测试用例:为了进一步探索这个问题,我创建了一个独立的测试用例,以清楚地识别出奇怪的行为。我假设任何可能正在测试此代码的人都已经设置了一个有效的解决方案并设置了dllexport / DLL_EXPORT / macros。我叫C。如果您需要最小的工作头文件,请告诉我。这里的主要应用程序位于C++/CLI并从32 bit dll调用函数。我使用 Visual Studio 2015 ,两个程序集都是void floatTest() { float floatValC = 42.42f; double doubleValC = 42.42; //even if passing the address, behaviour is same as all others. float retFloat = 42.42f; double retDouble = 42.42; int sizeOfFloatC = sizeof(float); int sizeOfDoubleC = sizeof(double); floatTestCPP(floatValC, doubleValC, &retFloat, &retDouble); //do some dummy math to make compiler happy (i.e. no unsused variable warnings) sizeOfFloatC = sizeOfFloatC + sizeOfDoubleC;//break point here }

我有点担心,因为我不确定这是否是我需要担心的事情,或者它只是调试器正在做的事情(我倾向于后者)。说实话,我对这里发生的事情完全好奇。

问题:任何人都可以解释观察到的行为或至少指出我的方向正确吗?

C - 调用函数

DLL_EXPORT void floatTestCPP(float floatVal, double doubleVal, 
      float *floatRet, double *doubleRet);

C ++ / CLI标题

//as you can see, there are no managed types in this function
void floatTestCPP(float floatVal, double doubleVal, float *floatRet, double *doubleRet)
{
    float floatLocal = floatVal;
    double doubleLocal = doubleVal;

    int sizeOfFloatCPP = sizeof(float);
    int sizeOfDoubleCPP = sizeof(double);

    *floatRet = 42.42f;
    *doubleRet = 42.42;

    //do some dummy math to make compiler happy (no warnings)
    floatLocal = (float)doubleLocal;//break point here
    sizeOfDoubleCPP = sizeOfFloatCPP;
}

C ++ / CLI来源

floatTest()

C中的调试器 - floatTestCPP()

的最后一行的断点

enter image description here

C ++ / CLI中的调试器 - DECLARE @QProcess VARCHAR(50) = 'Process 1' DECLARE @LProcess VARCHAR(50) = 'Process 2' ;WITH timeDifferences AS ( SELECT Q.ID AS QueueID, L.ID AS LogID, CASE WHEN ABS(DATEDIFF(MI,L.CreateDate,Q.DateCompleted)) <= 1 THEN ABS(DATEDIFF(MS,L.CreateDate,Q.DateCompleted)) END AS DiffInMS FROM #tmp_Job_Queue AS Q JOIN #tmp_Log AS L ON Q.ProcessName = @QProcess AND L.ProcessName = @LProcess ) SELECT * FROM timeDifferences AS T1 WHERE DiffInMS = (SELECT MIN(DiffInMS) FROM timeDifferences AS T2 WHERE T2.QueueID = T1.QueueID) GO 的倒数第二行的断点

enter image description here

1 个答案:

答案 0 :(得分:1)

考虑 C ++ / CLI中的调试器本身不一定用C,C#或C ++编码。

MS库支持"R" format:一个可以往返于相同数字的字符串。我怀疑这是使用了g格式。

没有MS源代码,以下只是一个很好的假设:

调试输出足以将double与其他double区分开来。所以代码无需打印"42.420000000000002",但"42.42"是足够 - 无论使用何种格式。

42.42作为IEEE double约为42.4200000000000017053025658242404460906982...,调试器当然不需要打印确切的值。

潜在;类似的C代码

int main(void) {
  puts("12.34567890123456");
  double d = 42.42;
  printf("%.16g\n", nextafter(d,0));
  printf("%.16g\n", d);
  printf("%.17g\n", d);
  printf("%.16g\n", nextafter(d,2*d));
  d = 1 / 3.0f;
  printf("%.9g\n", nextafterf(d,0));
  printf("%.9g\n", d);
  printf("%.9g\n", nextafterf(d,2*d));
  d = 1 / 3.0f;
  printf("%.16g\n", nextafter(d,0));
  printf("%.16g\n", d);
  printf("%.16g\n", nextafter(d,2*d));
}

输出

12.34567890123456
42.41999999999999
42.42
42.420000000000002   // this level of precision not needed.
42.42000000000001
0.333333313
0.333333343
0.333333373
0.3333333432674407
0.3333333432674408
0.3333333432674409

要使代码将double转换为具有足够文本精确度的文本并返回double以“往返”该数字,请参阅Printf width specifier to maintain precision of floating-point value