一点背景:我正在使用C#
中间层进行从C++/CLI
到floats
的一些数据转换,我注意到了它的特殊性调试器显示doubles
和C#
的方式,具体取决于代码执行的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()
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
的倒数第二行的断点
答案 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。