¿Cuál es la forma correcta de usar printf para imprimir un clock_t?
Pregunta
Actualmente estoy usando una conversión explícita a unsigned long long
y el uso de %llu
imprimirlo, pero desde size_t
tiene el especificador %z
, ¿por qué no clock_t
tener uno?
No hay ni siquiera una macro para ello. Tal vez pueda suponer que en un sistema x64 (OS y CPU) size_t
es de 8 bytes de longitud (e incluso en este caso, han proporcionado %z
), pero ¿qué pasa clock_t
?
Solución
No parece haber ninguna manera perfecta. La raíz del problema es que clock_t
puede ser entero o de coma flotante.
clock_t puede ser un tipo de coma flotante
Bastien Léonard menciona de POSIX (GO le upvote), C99 N1256 proyecto 7.23.1 / 3 dice también que:
[clock_t es] tipos aritméticas capaces de representar veces
y 6.2.5 / 18:
Entero y tipos flotantes se denominan colectivamente tipos aritméticos.
y el estándar define tipo aritmético, ya sea como números enteros o tipos de coma flotante.
Si va a dividir por CLOCKS_PER_SEC, el uso a largo doble
El valor de retorno de clock()
se define la aplicación, y la única manera de conseguir significado estándar fuera de él es dividir por CLOCKS_PER_SEC
para encontrar el número de segundos:
clock_t t0 = clock();
/* Work. */
clock_t t1 = clock();
printf("%Lf", (long double)(t1 - t0));
Esto es bastante bueno, aunque no es perfecto, por las dos razones siguientes:
-
no parece haber ningún análogo a
intmax_t
para este tipo de punto flotante: ¿Cómo obtener la mayor precisión flotante tipo de datos punto de implemenation y su especificador printf? lo tanto, si un tipo de coma flotante más grande sale mañana, podría ser usada y romper su aplicación. -
si
clock_t
es un número entero, el reparto a flotar está bien definida para utilizar el flotador más cercano posible. Es posible que pierda precisión, pero no importaría mucho en comparación con el valor absoluto, y sólo se le ocurriría a enormes cantidades de tiempo, por ejemplo,long int
en x86 es el flotador 80 bits con 64 bits significativos, que tiene millones de años en cuestión de segundos.
Ir upvote lemonad que dijo algo similar.
Si usted supone que es un entero, utiliza% ju y uintmax_t
Aunque unsigned long long
es actualmente el mayor tipo entero estándar posible:
- una más grande podría salir en el futuro
- explícitamente permite mayor aplicación definido tipos (felicitaciones a @FUZxxl) y
clock_t
podría ser uno de ellos
lo que es mejor para encasillado a la mayor tipo entero sin signo posible:
#include <stdint.h>
printf("%ju", (uintmax_t)(clock_t)1);
uintmax_t
está garantizado para tener el tamaño del mayor tamaño posible número entero en la máquina.
uintmax_t
y su %ju
printf especificador se introdujeron en c99 y gcc por ejemplo las implementa.
Como beneficio adicional, esto resuelve de una vez por todas la cuestión de cómo fiable printf
número entero tipos (que por desgracia no es el necesariamente el caso de clock_t
).
¿Qué podría salir mal si se trataba de un doble:
- si es demasiado grande para encajar en el número entero, comportamiento indefinido
- mucho menor que 1, conseguirá redondeado a 0 y no verá nada
Dado que esas consecuencias son mucho más duras que el número entero a flote la conversión, mediante flotador es probablemente una mejor idea.
En glibc 2.21 es un número entero
El manual dice que el uso de double
es una idea mejor:
En GNU / Linux y sistemas GNU / Hurd, clock_t es equivalente a largo int y CLOCKS_PER_SEC es un valor entero. Pero en otros sistemas, tanto clock_t y la macro CLOCKS_PER_SEC pueden ser ya sea entero o tipos de punto flotante. Fundición valores de tiempo de CPU para duplicar, como en el ejemplo anterior, se asegura de que las operaciones tales como la aritmética y la impresión trabajo correctamente y consiste delntly no importa lo que la representación subyacente es.
En glibc 2.21:
-
clock_t
eslong int
:- hora / time.h lo establece en
__clock_t
- bits / types.h lo establece en
__CLOCK_T_TYPE
- bits / typesizes.h lo establece en
__SLONGWORD_TYPE
- bits / types.h lo establece en
long int
- hora / time.h lo establece en
-
clock()
en Linux se implementa consys_clock_gettime
:- sysdeps / Unix / sysv / Linux / clock.c llama
__clock_gettime
- sysdeps / Unix / clock_gettime.c llama
SYSDEP_GETTIME_CPU
- sysdeps / Unix / sysv / Linux / clock_gettime.c llama
SYSCALL_GETTIME
que finalmente hace una llamada al sistema en línea
man clock_gettime
, nos dice que devuelve unstruct timespec
que a GCC contiene camposlong int
.Así que la implementación subyacente realmente devuelve enteros.
- sysdeps / Unix / sysv / Linux / clock.c llama
Ver también
Otros consejos
Por lo que yo sé, la forma en que está haciendo es la mejor. Excepto que clock_t
puede ser un tipo real:
time_t
yclock_t
serán entero o real flotante tipos.
http://www.opengroup.org/onlinepubs/ 009695399 / basedefs / sys / types.h.html
No es probablemente debido a impulsos del reloj es una unidad muy bien definido. Puede convertirlo en segundos e imprimirlo como un doble:
time_in_seconds = (double)time_in_clock_ticks / (double)CLOCKS_PER_SEC;
printf("%g seconds", seconds);
La macro CLOCKS_PER_SEC expande una expresión que representa el número de ciclos de reloj en un segundo.
El estándar C tiene que acomodar una amplia variedad de arquitecturas, lo que hace imposible hacer cualquier garantías adicionales aparte del hecho de que el tipo de reloj interno es aritmética.
En la mayoría de los casos, usted está interesado en intervalos de tiempo, así que me gustaría convertir la diferencia en el reloj avanza a milisegundos. Un unsigned long
es lo suficientemente grande como para representar un intervalo de casi 50 días, incluso si es de 32 bits, por lo que debe ser lo suficientemente grande para la mayoría de los casos:
clock_t start;
clock_t end;
unsigned long millis = (end - start) * 1000 / CLOCKS_PER_SEC;
Una forma es mediante el uso de la función gettimeofday
. Uno puede encontrar la diferencia de usar esta función:
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;
}