不准确的双打划分(Visual C ++ 2008)

时间:2009-05-23 19:48:57

标签: double division int64

我有一些代码可以将从QueryPerformanceCounter返回的时间值转换为以毫秒为单位的double值,因为这更便于计算。

该功能如下所示:

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

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

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

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

以下是我得到的一些结果。程序是在Debug模式下编译的,C ++选项中的Floating Point模式设置为默认值(Precise(/ 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窗口中仍然是正确的。 对于它的结果,它是否使用更高精度的类型而不是double?

2 个答案:

答案 0 :(得分:0)

Adion,

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

您对数字的大小是正确的。它抛弃了浮点计算的准确性。

有关这方面的更多信息,请参阅:

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

答案 1 :(得分:0)

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

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

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();
}