2009-05-23 2 views
1

У меня есть код для преобразования значения времени, возвращаемого из QueryPerformanceCounter, в двойное значение в миллисекундах, так как это удобнее подсчитывать.Неточное разделение удвоений (Visual C++ 2008)

Функция выглядит следующим образом:

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++ параметры было установлен по умолчанию (Precise (/ FP: точный))

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 отладчика по сравнению с привести к моей программе?

Обновление: Я попытался вычитать 30270310439445 с помощью функции timerPerformanceCounter.QuadPart перед выполнением преобразования и деления, и теперь он кажется точным во всех случаях. Возможно, причина, по которой я сейчас вижу это поведение, может быть связана с тем, что время работы моего компьютера составляет 16 дней, поэтому значение больше, чем я привык? Таким образом, это похоже на проблему точности разделения с большими номерами, но это все еще не объясняет, почему разделение по-прежнему было правильным в окне «Часы». Использует ли он более высокоточный тип, чем двойной для его результатов?

ответ

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

Adion,

Если вы не возражаете, падение производительности, отлитый ваши номера QuadPart в десятичную вместо двойной перед выполнением разделения. Затем верните результирующее число в двойное.

Вы ввели неправильный размер цифр. Это сбрасывает точность вычислений с плавающей запятой.

Более подробно об этом, чем вы, вероятно, когда-либо хотели знать, см:

Что каждый ученый должен знать о арифметики с плавающей точкой http://docs.sun.com/source/806-3568/ncg_goldberg.html