Quelle est la bonne façon d'utiliser printf pour imprimer un clock_t?
Question
J'utilise actuellement un casting explicite à unsigned long long
et en utilisant %llu
pour l'imprimer, mais depuis size_t
a le spécificateur %z
, pourquoi ne clock_t
pas un?
Il n'y a même pas une macro pour elle. Peut-être que je peux supposer que sur un système x64 (OS et CPU) size_t
est de 8 octets de longueur (et même dans ce cas, ils ont fourni %z
), mais qu'en est-clock_t
?
La solution
Il semble y avoir aucun moyen parfait. La racine du problème est que clock_t
peut être entier ou à virgule flottante.
clock_t peut être un type à virgule flottante
Bastien Léonard mentionne Posix (aller le upvote), C99 projet N1256 7.23.1 / 3 dit aussi que:
[clock_t est] types arithmétiques capables de représenter les temps
et 6.2.5 / 18:
Entier et les types flottants sont collectivement appelés types arithmétiques.
et la norme définit le type arithmétique soit comme des entiers ou des types à virgule flottante.
Si vous divisez par CLOCKS_PER_SEC, utilisez long double
La valeur de retour de clock()
est défini par l'implémentation, et la seule façon d'obtenir le sens de la norme est de 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));
Ceci est assez bon, mais pas parfait, pour les deux raisons suivantes:
-
il ne semble pas analogue à
intmax_t
pour les types de virgule flottante: Comment obtenir le plus grand type de données à virgule flottante précision de implemenation et son spécificateur printf? donc, si un type de point flottant plus grand sort demain, il pourrait être utilisé et briser votre mise en œuvre. -
si
clock_t
est un entier, la distribution de flotter est bien défini pour utiliser le flotteur le plus proche possible. Vous risquez de perdre la précision, mais il ne serait pas beaucoup d'importance par rapport à la valeur absolue, et ne se produirait d'énormes quantités de temps, par exemplelong int
en x86 est le flotteur 80 bits avec 64 bits significatif, ce qui est des millions d'années en quelques secondes.
Aller upvote Lemonad qui a dit quelque chose de similaire.
Si vous pensez qu'il est un entier, utilisez% ju et uintmax_t
Bien que unsigned long long
est actuellement le plus grand type entier niveau possible:
- une plus grande pourrait sortir à l'avenir
- autorise explicitement types définis par la mise en œuvre plus large (kudos à @FUZxxl) et
clock_t
pourrait être l'un d'entre eux
il est donc préférable de typer le plus grand entier non signé possible:
#include <stdint.h>
printf("%ju", (uintmax_t)(clock_t)1);
uintmax_t
est garanti d'avoir la taille de la plus grande taille entière possible sur la machine.
uintmax_t
et son spécificateur printf %ju
ont été introduits dans C99 et gcc par exemple les met en œuvre.
En prime, cela résout une fois pour toutes la question de savoir comment les types entier de manière fiable printf
(ce qui est malheureusement pas nécessairement le cas pour clock_t
).
Qu'est-ce qui pourrait mal tourner si elle était double:
- si elle est trop volumineux pour tenir dans l'entier, un comportement non défini
- beaucoup plus petit que 1, va s'arrondi à 0 et vous ne verrez rien
Étant donné que ces conséquences sont beaucoup plus dures que l'entier de flotter la conversion, en utilisant float est probablement une meilleure idée.
Sur la glibc 2.21 est un entier
Le manuel dit que l'utilisation double
est une meilleure idée:
Sur GNU / Linux et les systèmes GNU / Hurd, clock_t est équivalent à long int et CLOCKS_PER_SEC est une valeur entière. Mais dans d'autres systèmes, à la fois clock_t et la macro CLOCKS_PER_SEC peuvent être soit entier ou types à virgule flottante. Casting valeurs de temps CPU pour doubler, comme dans l'exemple ci-dessus, fait en sorte que des opérations telles que le travail de l'arithmétique et l'impression correctement et Consistently quelle que soit la représentation sous-jacente est.
Dans la glibc 2.21:
-
clock_t
estlong int
:- le temps / time.h fixe à
__clock_t
- bits / types.h fixe à
__CLOCK_T_TYPE
- bits / typesizes.h fixe à
__SLONGWORD_TYPE
- bits / types.h fixe à
long int
- le temps / time.h fixe à
-
clock()
sous Linux est mis en œuvre avecsys_clock_gettime
:- sysdeps / unix / sysv / linux / clock.c appelle
__clock_gettime
- sysdeps / unix / clock_gettime.c appelle
SYSDEP_GETTIME_CPU
- sysdeps / unix / sysv / linux / clock_gettime.c appelle
SYSCALL_GETTIME
qui fait enfin un appel système en ligne
man clock_gettime
, nous dit qu'il retourne unstruct timespec
qui GCC contient des champs delong int
.la mise en œuvre sous-jacente retourne vraiment des nombres entiers.
- sysdeps / unix / sysv / linux / clock.c appelle
Voir aussi
Autres conseils
Pour autant que je sache, la façon dont vous faites est le meilleur. Sauf que clock_t
peut être un type réel:
time_t
etclock_t
sont entier ou types flottant réel.
http://www.opengroup.org/onlinepubs/ 009695399 / basedefs / sys / types.h.html
Il est probablement parce que les tiques horloge n'est pas une unité très bien définie. Vous pouvez le convertir en quelques 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 développe en une expression représentant le nombre d'impulsions d'horloge dans une seconde.
La norme C doit accueillir une grande variété d'architectures, ce qui rend impossible de faire d'autres garanties à part le fait que le type d'horloge interne est arithmétique.
Dans la plupart des cas, vous êtes intéressé par des intervalles de temps, donc je convertir la différence de l'horloge impulsions en millisecondes. Un unsigned long
est assez grand pour représenter un intervalle de près de 50 jours, même si son 32bit, 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 façon est en utilisant 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;
}