J'utilise actuellement une conversion explicite en unsigned long long
et en utilisant %llu
pour l'imprimer, mais puisque size_t
a le spécificateur %z
, pourquoi clock_t
n'en a-t-il pas?
Il n'y a même pas de macro pour cela. Je peux peut-être supposer que sur un système x64 (système d'exploitation et processeur), size_t
a une longueur de 8 octets (et même dans ce cas, ils ont fourni %z
), mais qu'en est-il de clock_t
?
Il semble y avoir aucun moyen parfait. La racine du problème est que clock_t
peut être un entier ou une virgule flottante.
clock_t peut être un type à virgule flottante
Comme Bastien Léonard mentionne pour POSIX (le vote se lève), C99 N1256 draft 7.23.1/3 dit aussi que:
[clock_t est] des types arithmétiques capables de représenter des temps
et 6.2.5/18:
Les types entier et flottant sont collectivement appelés types arithmétiques.
et la norme définit le type arithmétique comme des types entiers ou à virgule flottante.
Si vous voulez diviser par CLOCKS_PER_SEC, utilisez long double
La valeur de retour de clock()
est définie par l’implémentation. Le seul moyen d’en extraire la signification standard consiste à diviser par CLOCKS_PER_SEC
pour trouver le nombre de secondes:
clock_t t0 = clock();
/* Work. */
clock_t t1 = clock();
printf("%Lf", (long double)(t1 - t0));
C'est assez bon, bien que pas parfait, pour les deux raisons suivantes:
il ne semble exister aucun analogue à intmax_t
pour les types à virgule flottante: Comment obtenir le type de données à virgule flottante avec la plus grande précision et son spécificateur printf? Donc si un type plus grand en virgule flottante sort demain, il pourrait être utilisé et casser votre implémentation.
si clock_t
est un entier, la conversion en float est bien définie pour utiliser le float le plus proche possible. Vous perdrez peut-être en précision, mais cela n’aurait pas beaucoup d’importance par rapport à la valeur absolue, et cela ne se produirait que très longtemps, par exemple. long int
en x86 est le nombre flottant de 80 bits avec une valeur significative de 64 bits, ce qui correspond à des millions d'années en secondes.
Go upvote lemonad qui a dit quelque chose de similaire.
Si vous supposez que c'est un entier, utilisez% ju et uintmax_t
Bien que unsigned long long
soit actuellement le type d’entier standard le plus grand possible:
clock_t
pourrait être l'un d'entre euxil est donc préférable de définir le plus grand type d'entier non signé possible:
#include <stdint.h>
printf("%ju", (uintmax_t)(clock_t)1);
uintmax_t
est garanti pour avoir la taille du plus grand nombre entier possible sur la machine.
uintmax_t
et son spécificateur printf %ju
ont été introduits dans c99 et gcc par exemple les implémente.
En prime, cela résout une fois pour toutes la question de savoir comment utiliser de manière fiable les types entiers printf
(ce qui n’est malheureusement pas nécessairement le cas pour clock_t
).
Qu'est-ce qui pourrait mal tourner si c'était un double:
Étant donné que ces conséquences sont beaucoup plus sévères que la conversion de nombre entier en float, l’utilisation de float est probablement une meilleure idée.
Sur la glibc 2.21 c'est un entier
Le manuel dit que double
est une meilleure idée:
Sur les systèmes GNU/Linux et GNU/Hurd, clock_t est équivalent à long int et CLOCKS_PER_SEC est une valeur entière. Mais dans d’autres systèmes, clock_t et la macro CLOCKS_PER_SEC peuvent être de type entier ou à virgule flottante. En doublant les valeurs de temps CPU, comme dans l'exemple ci-dessus, vous vous assurez que des opérations telles que l'arithmétique et l'impression fonctionnent correctement et de manière cohérente, quelle que soit la représentation sous-jacente.
En glibc 2.21:
clock_t
est long int
:
__clock_t
__CLOCK_T_TYPE
__SLONGWORD_TYPE
long int
clock()
sous Linux est implémenté avec sys_clock_gettime
:
__clock_gettime
SYSDEP_GETTIME_CPU
SYSCALL_GETTIME
qui effectue un appel système en ligneman clock_gettime
, nous dit qu'il renvoie un struct timespec
qui, dans GCC, contient des champs long int
.
Donc, l'implémentation sous-jacente renvoie vraiment des entiers.
Voir également
C'est probablement parce que les tics d'horloge ne sont pas une unité très bien définie. Vous pouvez le convertir en secondes et l’imprimer en double:
time_in_seconds = (double)time_in_clock_ticks / (double)CLOCKS_PER_SEC;
printf("%g seconds", seconds);
La macro CLOCKS_PER_SEC se développe en une expression représentant le nombre de ticks d'horloge par seconde.
Autant que je sache, votre façon de faire est la meilleure. Sauf que clock_t
peut être un type réel:
time_t
etclock_t
doivent être des types entiers ou réels.
http://www.opengroup.org/onlinepubs/009695399/basedefs/sys/types.h.html
La norme C doit s'adapter à une grande variété d'architectures, ce qui rend impossible toute garantie supplémentaire en dehors du fait que le type d'horloge interne est arithmétique.
Dans la plupart des cas, les intervalles de temps vous intéressent, je convertirais donc la différence en ticks d'horloge en millisecondes. Un unsigned long
est assez grand pour représenter un intervalle de près de 50 jours, même s'il est 32 bits, il devrait donc être assez grand pour la plupart des cas:
clock_t start;
clock_t end;
unsigned long millis = (end - start) * 1000 / CLOCKS_PER_SEC;
Une solution consiste à utiliser la fonction gettimeofday
. On peut trouver la différence en utilisant cette fonction:
unsigned long diff(struct timeval second, struct timeval first)
{
struct timeval lapsed;
struct timezone tzp;
unsigned long t;
if (first.tv_usec > second.tv_usec) {
second.tv_usec += 1000000;
second.tv_sec--;
}
lapsed.tv_usec = second.tv_usec - first.tv_usec;
lapsed.tv_sec = second.tv_sec - first.tv_sec;
t = lapsed.tv_sec*1000000 + lapsed.tv_usec;
printf("%lu,%lu - %lu,%lu = %ld,%ld\n",
second.tv_sec, second.tv_usec,
first.tv_sec, first.tv_usec,
lapsed.tv_sec, lapsed.tv_usec);
return t;
}