1

我有一些代码可以将 QueryPerformanceCounter 返回的时间值转换为以毫秒为单位的双精度值,因为这样计算起来更方便。

该函数如下所示:

double timeGetExactTime() {
    LARGE_INTEGER timerPerformanceCounter, timerPerformanceFrequency;
    QueryPerformanceCounter(&timerPerformanceCounter);
    if (QueryPerformanceFrequency(&timerPerformanceFrequency)) {
        return (double)timerPerformanceCounter.QuadPart / (((double)timerPerformanceFrequency.QuadPart) / 1000.0);
    }
    return 0.0;
}

我最近遇到的问题(我认为我之前没有遇到过这个问题,并且没有对代码进行任何更改)是结果不是很准确。结果不包含任何小数,但精确度甚至低于 1 毫秒。

当我在调试器中输入表达式时,结果与我预期的一样准确。

我知道双精度不能保持 64 位整数的精度,但此时,PerformanceCounter 只需要 46 位(双精度应该能够存储 52 位而不会丢失)此外,调试器使用似乎很奇怪用不同的格式来做除法。

这是我得到的一些结果。该程序在调试模式下编译,C++ 选项中的浮点模式设置为默认值(精确 (/fp:precise) )

timerPerformanceCounter.QuadPart: 30270310439445
timerPerformanceFrequency.QuadPart: 14318180
double perfCounter = (double)timerPerformanceCounter.QuadPart;
30270310439445.000

double perfFrequency = (((double)timerPerformanceFrequency.QuadPart) / 1000.0);
14318.179687500000

double result = perfCounter / perfFrequency;
2114117248.0000000

return (double)timerPerformanceCounter.QuadPart / (((double)timerPerformanceFrequency.QuadPart) / 1000.0);
2114117248.0000000

Result with same expression in debugger:
2114117188.0396111

Result of perfTimerCount / perfTimerFreq in debugger:
2114117234.1810646

Result of 30270310439445 / 14318180 in calculator:
2114117188.0396111796331656677036

有谁知道为什么调试器 Watch 中的精度与我的程序中的结果不同?

更新:在进行转换和除法之前,我尝试从 timerPerformanceCounter.QuadPart 中减去 30270310439445,现在它在所有情况下似乎都是准确的。也许我现在只看到这种行为的原因可能是因为我的计算机的正常运行时间现在是 16 天,所以值比我习惯的要大?所以这似乎是一个大数字的划分精度问题,但这仍然不能解释为什么在 Watch 窗口中划分仍然正确。它的结果是否使用比双精度更高的类型?

4

2 回答 2

0

阿迪翁,

如果您不介意性能下降,请在执行除法之前将您的 QuadPart 数字转换为十进制而不是两倍。然后将结果数字转换回两倍。

您对数字的大小是正确的。它会降低浮点计算的准确性。

有关这方面的更多信息,您可能想知道,请参阅:

每个计算机科学家都应该了解浮点运算 http://docs.sun.com/source/806-3568/ncg_goldberg.html

于 2009-05-23T20:59:13.640 回答
0

谢谢,使用十进制也可能是一个解决方案。现在我采取了一种稍微不同的方法,它也很有效,至少只要我的程序在不重新启动的情况下运行时间不超过一周左右。我只记得我的程序启动时的性能计数器,并在转换为双精度并进行除法之前从当前计数器中减去它。

我不确定哪种解决方案最快,我想我必须先对其进行基准测试。

bool perfTimerInitialized = false;
double timerPerformanceFrequencyDbl;
LARGE_INTEGER timerPerformanceFrequency;
LARGE_INTEGER timerPerformanceCounterStart;
double timeGetExactTime()
{
    if (!perfTimerInitialized) {
        QueryPerformanceFrequency(&timerPerformanceFrequency);
        timerPerformanceFrequencyDbl = ((double)timerPerformanceFrequency.QuadPart) / 1000.0;
        QueryPerformanceCounter(&timerPerformanceCounterStart);
        perfTimerInitialized = true;
    }

    LARGE_INTEGER timerPerformanceCounter;
    if (QueryPerformanceCounter(&timerPerformanceCounter)) {
        timerPerformanceCounter.QuadPart -= timerPerformanceCounterStart.QuadPart;
        return ((double)timerPerformanceCounter.QuadPart) / timerPerformanceFrequencyDbl;
    }

    return (double)timeGetTime();
}
于 2009-05-24T09:00:57.953 回答