Pregunta

Tengo un código para convertir un valor de tiempo devuelto por QueryPerformanceCounter a un valor doble en milisegundos, ya que es más conveniente contar con él.

La función se ve así:

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

El problema que estoy teniendo recientemente (no creo haber tenido este problema antes y no se han realizado cambios en el código) es que el resultado no es muy preciso.El resultado no contiene decimales, pero es incluso menos preciso que 1 milisegundo.

Cuando ingreso la expresión en el depurador, el resultado es tan preciso como esperaría.

Entiendo que un doble no puede contener la precisión de un entero de 64 bits, pero en este momento, el rendimiento de la actuación solo requirió 46 bits (y un doble debería poder almacenar 52 bits sin pérdida). Parece extraño que el depurador use un formato diferente para hacer la división.

Aquí hay algunos resultados que obtuve.El programa se compiló en modo de depuración, el modo de punto flotante en las opciones de C++ se configuró en el valor predeterminado (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

¿Alguien sabe por qué la precisión es diferente en el reloj del depurador en comparación con el resultado de mi programa?

Actualizar:Intenté deducir 30270310439445 de timerPerformanceCounter.QuadPart antes de realizar la conversión y división, y ahora parece ser exacto en todos los casos.Tal vez la razón por la que solo veo este comportamiento ahora podría ser porque el tiempo de actividad de mi computadora ahora es de 16 días, por lo que el valor es mayor de lo que estoy acostumbrado.Por lo tanto, parece ser un problema de precisión de la división con números grandes, pero eso aún no explica por qué la división seguía siendo correcta en la ventana Inspección.¿Utiliza un tipo de precisión superior al doble para sus resultados?

¿Fue útil?

Solución 2

Gracias, usar decimal probablemente también sería una solución.Por ahora he adoptado un enfoque ligeramente diferente, que también funciona bien, al menos siempre y cuando mi programa no se ejecute más de una semana sin reiniciar.Simplemente recuerdo el contador de rendimiento de cuando comenzó mi programa y lo resto del contador actual antes de convertirlo a doble y hacer la división.

No estoy seguro de qué solución sería más rápida, supongo que primero tendría que compararla.

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

Otros consejos

ADION,

Si no te importa el impacto en el rendimiento, emitir sus números QuadPart a decimal en lugar de doble antes de realizar la división. Entonces emitir el número resultante de nuevo a doble.

Tiene razón sobre el tamaño de los números. Se despoja de la exactitud de los cálculos de punto flotante.

Para más de esto que es probable que siempre quiso saber, ver:

Lo que todo informático debe saber sobre la aritmética de punto flotante http://docs.sun.com/source/806-3568/ncg_goldberg. html

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top