2009-05-23 10 views
1

Ich habe einen Code, um einen von QueryPerformanceCounter zurückgegebenen Zeitwert in einen doppelten Wert in Millisekunden zu konvertieren, da dies einfacher zu zählen ist.Ungenaue Division von Doppelpunkten (Visual C++ 2008)

Die Funktion sieht wie folgt aus:

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

Das Problem, das ich vor kurzem mit bin (ich glaube nicht, ich habe dieses Problem vor, und keine Änderungen wurden an den Code vorgenommen) ist, dass das Ergebnis ist nicht sehr genau. Das Ergebnis enthält keine Dezimalstellen, aber es ist noch weniger genau als 1 Millisekunde.

Wenn ich den Ausdruck in dem Debugger eingeben, ist das Ergebnis so genau, wie ich erwarten würde.

Ich verstehe, dass ein Doppel nicht die Genauigkeit einer 64-Bit-Ganzzahl halten kann, aber der PerformanceCounter zu diesem Zeitpunkt nur 46 Bit benötigt (und ein Doppel sollte 52 Bits ohne Verlust speichern können) Darüber hinaus scheint es seltsam dass der Debugger ein anderes Format für die Division verwenden würde.

Hier sind einige Ergebnisse, die ich bekommen habe. Das Programm wurde im Debug-Modus kompiliert, Floating-Point-Modus in C++ Optionen wurde auf die Standardeinstellung (Precise (/ fp: präzise)) gesetzt

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 

Weiß jemand, warum die Genauigkeit in dem Debugger Watch im Vergleich zu dem anders ist Ergebnis in meinem Programm?

Update: Ich habe versucht, 30270310439445 von TimerPerformanceCounter.QuadPart abziehen, bevor Sie die Konvertierung und Division, und es scheint jetzt in allen Fällen genau zu sein. Vielleicht liegt der Grund, warum ich nur dieses Verhalten jetzt sehe, möglicherweise darin, dass die Betriebszeit meines Computers jetzt 16 Tage beträgt, also ist der Wert größer als ich es gewohnt bin? Es scheint also ein Problem mit der Divisionspräzision bei großen Zahlen zu sein, aber das erklärt immer noch nicht, warum die Division im Watch-Fenster noch korrekt war. Verwendet es für seine Ergebnisse einen Typ mit höherer Genauigkeit als doppelt?

Antwort

0

Danke, dezimal mit würde wahrscheinlich auch eine Lösung sein. Für jetzt habe ich einen etwas anderen Ansatz genommen, der auch gut funktioniert, zumindest solange mein Programm nicht länger als eine Woche oder so läuft, ohne neu zu starten. Ich erinnere mich nur an den Leistungszähler, als mein Programm gestartet wurde, und subtrahiere dies vom aktuellen Zähler, bevor ich in den Doppelgänger umwandle und die Division mache.

Ich bin mir nicht sicher, welche Lösung am schnellsten wäre, ich denke, ich müsste das zuerst benchmarken.

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,

Wenn Sie die Performance-Einbußen nichts dagegen haben, werfen Sie Ihre Quadpart Zahlen anstelle von Doppel dezimal bevor die Division durchgeführt wird. Dann werfen Sie die resultierende Zahl zurück auf das Doppelte.

Sie sind richtig über die Größe der Zahlen. Es wirft die Genauigkeit der Fließkommaberechnungen weg.

Für mehr darüber, als Sie wahrscheinlich schon immer mal wissen wollte, siehe:

Was jeder Informatiker wissen sollten über Gleitkommaarithmetik http://docs.sun.com/source/806-3568/ncg_goldberg.html