Qual è il modo corretto di usare printf per stampare una clock_t?
Domanda
Attualmente sto usando un cast esplicito a unsigned long long
e utilizzando %llu
stamparlo, ma dal momento che size_t
ha l'identificatore %z
, perché non clock_t
averne uno?
Non c'è nemmeno una macro per esso. Forse posso supporre che su un sistema x64 (OS e CPU) size_t
è di 8 byte di lunghezza (e anche in questo caso, hanno fornito %z
), ma per quanto riguarda clock_t
?
Soluzione
Non sembra esserci alcun modo perfetto. La radice del problema è che clock_t
può essere sia interi o in virgola mobile.
clock_t può essere un tipo a virgola mobile
Bastien Léonard menziona per POSIX (GO lo upvote), C99 N1256 progetto 7.23.1 / 3 dice anche che:
[clock_t è] tipi aritmetici grado di rappresentare volte
e 6.2.5 / 18:
integer e tipi galleggianti sono collettivamente chiamati tipi aritmetici.
e lo standard definisce tipo aritmetico sia come interi o tipi in virgola mobile.
Se si intende dividere per CLOCKS_PER_SEC, utilizzare lungo doppio
Il valore di ritorno di clock()
è implementazione definita, e l'unico modo per ottenere significato standard di fuori di esso è quello di dividere per CLOCKS_PER_SEC
per trovare il numero di secondi:
clock_t t0 = clock();
/* Work. */
clock_t t1 = clock();
printf("%Lf", (long double)(t1 - t0));
Questo è abbastanza buono, anche se non perfetto, per i due seguenti motivi:
-
non sembra esserci alcun analogica a
intmax_t
per il galleggiamento tipi di punti: Come ottenere il maggior precisione floating point tipo di dati di implemenation e il suo identificatore printf? Quindi, se un tipo in virgola mobile più grande esce domani, potrebbe essere Usato e rompere l'implementazione. -
se
clock_t
è un numero intero, il cast galleggiare è ben definito per utilizzare il galleggiante più vicino possibile. Si può perdere la precisione, ma non importerebbe molto rispetto al valore assoluto, e sarebbe accadere solo per enormi quantità di tempo, per esempiolong int
nel x86 è il galleggiante 80-bit con 64 bit significativo, che è milioni di anni in pochi secondi.
Vai upvote Lemonad che ha detto qualcosa di simile.
Se pensate che è un numero intero, utilizzare% ju e uintmax_t
Sebbene unsigned long long
è attualmente il più grande tipo intero livello possibile:
- una più grande potrebbe venire in futuro
- esplicitamente permette più grande implementazione definiti tipi (complimenti a @FUZxxl) e
clock_t
potrebbe essere uno di loro
quindi è meglio typecast alla grande tipo intero senza segno possibile:
#include <stdint.h>
printf("%ju", (uintmax_t)(clock_t)1);
uintmax_t
si garantisce la dimensione più grande possibile intero sulla macchina.
uintmax_t
e la sua %ju
printf identificatore sono stati introdotti nel C99 e gcc per esempio li implementa.
Come bonus, questo risolve una volta per tutte la questione di come affidabile printf
intero tipi (che non è, purtroppo, l'necessariamente il caso per clock_t
).
Che cosa potrebbe andare male se si trattava di una doppia:
- se troppo grande per adattarsi al numero intero, un comportamento indefinito
- molto più piccolo di 1, otterrà arrotondato a 0 e non si vede niente
Tali conseguenze sono molto più severe rispetto al numero intero di galleggiare conversione, utilizzando galleggiante è probabile un'idea migliore.
In glibc 2.21 è un numero intero
Il manuale dice che l'uso double
è un'idea migliore:
Sui sistemi GNU / Hurd GNU / Linux e, clock_t equivale a long int e CLOCKS_PER_SEC è un valore intero. Ma in altri sistemi, sia clock_t e la macro CLOCKS_PER_SEC possono essere sia interi o tipi a virgola mobile. Casting valori di tempo di CPU per raddoppiare, come nell'esempio di cui sopra, fa in modo che le operazioni come l'aritmetica e la stampa il lavoro correttamente e del consistente me nte indipendentemente dalla rappresentazione sottostante è.
In glibc 2.21:
-
clock_t
èlong int
:- tempo / time.h imposta su
__clock_t
- bit / types.h imposta su
__CLOCK_T_TYPE
- bit / typesizes.h imposta su
__SLONGWORD_TYPE
- bit / types.h imposta su
long int
- tempo / time.h imposta su
-
clock()
in Linux è implementato consys_clock_gettime
:- sysdeps / unix / sysv / linux / clock.c chiama
__clock_gettime
- sysdeps / unix / clock_gettime.c chiama
SYSDEP_GETTIME_CPU
- sysdeps / unix / sysv / linux / clock_gettime.c chiama
SYSCALL_GETTIME
che rende finalmente una chiamata di sistema in linea
man clock_gettime
, ci dice che restituisce unstruct timespec
che a GCC contiene i campilong int
.Quindi, l'implementazione sottostante torna davvero interi.
- sysdeps / unix / sysv / linux / clock.c chiama
Vedi anche
Altri suggerimenti
Per quanto ne so, il modo in cui si sta facendo è il migliore. Solo che clock_t
può essere un vero e proprio tipo:
time_t
eclock_t
sono interi o reale flottante tipi.
http://www.opengroup.org/onlinepubs/ 009695399 / basedefs / sys / types.h.html
E 'probabilmente perché clock ticks non è un'unità molto ben definito. È possibile convertirlo in secondi e stamparlo come un doppio:
time_in_seconds = (double)time_in_clock_ticks / (double)CLOCKS_PER_SEC;
printf("%g seconds", seconds);
La macro CLOCKS_PER_SEC espande un'espressione che rappresenta il numero di impulsi di clock in un secondo.
Lo standard C deve accogliere un'ampia varietà di architetture, che rende impossibile effettuare ulteriori garanzie a parte il fatto che il tipo di orologio interno è aritmetica.
Nella maggior parte dei casi, siete interessati a intervalli di tempo, quindi mi piacerebbe convertire la differenza di clock ticks a millisecondi. Un unsigned long
è grande abbastanza per rappresentare un intervallo di quasi 50 giorni, anche se i suoi 32 bit, quindi dovrebbe essere abbastanza grande per la maggior parte dei casi:
clock_t start;
clock_t end;
unsigned long millis = (end - start) * 1000 / CLOCKS_PER_SEC;
Un modo è quello di utilizzare la funzione gettimeofday
. Si può trovare la differenza di utilizzare questa funzione:
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;
}