2009-05-23 10 views
1

J'ai un code pour convertir une valeur de temps renvoyée par QueryPerformanceCounter en une valeur double en millisecondes, car il est plus pratique de compter avec.Division inexacte des doubles (Visual C++ 2008)

La fonction ressemble à ceci:

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

Le problème que je vais avoir récemment (je ne pense pas que j'ai eu ce problème avant, et aucune modification n'a été apportée au code) est que le résultat n'est pas très précis. Le résultat ne contient pas de décimales, mais il est encore moins précis que 1 milliseconde.

Lorsque j'entre l'expression dans le débogueur, le résultat est aussi précis que je le pensais. Je comprends qu'un double ne peut pas contenir la précision d'un nombre entier de 64 bits, mais à ce moment, le PerformanceCounter ne demandait que 46 bits (et un double devrait pouvoir stocker 52 bits sans perte) En outre, il semble étrange que le débogueur utiliserait un format différent pour faire la division.

Voici quelques résultats que j'ai obtenus. Le programme a été compilé en mode débogage, flottant en mode point dans les options C++ a été défini sur la valeur par défaut (Précision (/ fp: précis))

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 

Est-ce que quelqu'un sait pourquoi la précision est différente Montre le débogueur par rapport à la aboutir à mon programme?

Mise à jour: J'ai essayé de déduire 30270310439445 de timerPerformanceCounter.QuadPart avant de faire la conversion et la division, et il semble être précis dans tous les cas maintenant. Peut-être que la raison pour laquelle je ne vois que ce comportement est peut-être que le temps de disponibilité de mon ordinateur est maintenant de 16 jours, donc la valeur est plus grande que ce à quoi je suis habitué? Il semble donc que ce soit un problème de précision de division avec de grands nombres, mais cela n'explique toujours pas pourquoi la division était toujours correcte dans la fenêtre de surveillance. Utilise-t-il un type de plus haute précision que le double pour ses résultats?

Répondre

0

Merci, en utilisant décimal serait probablement une solution aussi. Pour l'instant, j'ai adopté une approche légèrement différente, qui fonctionne aussi bien, du moins tant que mon programme ne dure pas plus d'une semaine sans redémarrer. Je me souviens juste du compteur de performance de quand mon programme a commencé, et soustrais cela du compteur actuel avant de convertir en double et de faire la division.

Je ne sais pas quelle solution serait la plus rapide, je suppose que je devrais d'abord comparer cela.

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,

Si vous ne me dérange pas le coup de performance, jeter vos numéros de QuadPart en décimal au lieu de la double avant d'effectuer la division. Ensuite, retournez le nombre résultant à doubler.

Vous avez raison concernant la taille des chiffres. Il rejette la précision des calculs en virgule flottante.

Pour en savoir plus sur ce que vous avez probablement jamais voulu savoir, voir:

Ce que tout informaticien devrait savoir sur Arithmétique flottante http://docs.sun.com/source/806-3568/ncg_goldberg.html