c io printf clock

¿Cuál es la forma correcta de usar printf para imprimir un clock_t?



io (5)

El estándar C tiene que acomodar una amplia variedad de arquitecturas, lo que hace que sea imposible hacer más garantías, aparte del hecho de que el tipo de reloj interno es aritmético.

En la mayoría de los casos, le interesan los intervalos de tiempo, por lo que convertiría la diferencia en marcas de reloj en 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 debería 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;

Actualmente estoy usando un molde explícito para unsigned long long y usando %llu para imprimirlo, pero como size_t tiene el especificador %z , ¿por qué clock_t no tiene uno?

Ni siquiera hay una macro para eso. Quizás pueda suponer que en un sistema x64 (SO y CPU) size_t tiene una longitud de 8 bytes (e incluso en este caso, han proporcionado %z ), pero ¿qué pasa con clock_t ?



Parece que no hay una manera perfecta. La raíz del problema es que clock_t puede ser un número entero o coma flotante.

clock_t puede ser un tipo de coma flotante

Como Bastien Léonard menciona para POSIX (váyelo a él arriba ), C99 N1256, borrador 7.23.1 / 3 también dice que:

[clock_t is] tipos aritméticos capaces de representar los tiempos

y 6.2.5 / 18:

Los tipos enteros y flotantes se denominan colectivamente tipos aritméticos.

y el estándar define el tipo aritmético como enteros o tipos de punto flotante.

Si va a dividir por CLOCKS_PER_SEC, use el doble largo

El valor de retorno de clock() es la implementación definida, y la única forma de obtener un significado estándar 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 lo suficientemente bueno, aunque no perfecto, por las dos siguientes razones:

  • parece que no hay ningún análogo a intmax_t para los tipos de coma flotante: ¿Cómo obtener el tipo de implemento de datos flotantes de precisión más grande y su especificador de impresión? Entonces, si sale un tipo de punto flotante más grande mañana, podría usarse y romper su implementación.

  • si clock_t es un número entero, el molde para flotar está bien definido para usar el flotador más cercano posible. Puede perder precisión, pero no importaría mucho en comparación con el valor absoluto, y solo ocurriría por grandes cantidades de tiempo, por ejemplo, long int en x86 es el flotante de 80 bits con 64 bits significativo, que es millones de años en segundos.

Vaya arriba votó, que dijo algo similar.

Si supone que es un número entero, use% ju y uintmax_t

Aunque unsigned long long es actualmente el tipo entero estándar más grande posible:

  • uno más grande podría salir en el futuro
  • el estándar ya permite explícitamente tipos definidos de implementación más grandes (felicitaciones a @FUZxxl) y clock_t podría ser uno de ellos

por lo que es mejor encasillar al tipo de entero más grande sin signo posible:

#include <stdint.h> printf("%ju", (uintmax_t)(clock_t)1);

uintmax_t está garantizado para tener el tamaño del tamaño entero más grande posible en la máquina.

uintmax_t y su especificador printf %ju se introdujeron en c99 y gcc, por ejemplo, los implementa.

Como beneficio adicional, esto resuelve de una vez por todas la cuestión de cómo clock_t de manera confiable los tipos enteros (que desafortunadamente no es necesariamente el caso de clock_t ).

¿Qué podría salir mal si fuera un doble?

  • si es demasiado grande para caber en el entero, comportamiento indefinido
  • mucho más pequeño que 1, se redondeará a 0 y no verás nada

Dado que esas consecuencias son mucho más duras que la conversión de entero para flotar, es probable que usar flotación sea una mejor idea.

En glibc 2.21 es un número entero

El manual dice que usar el double es una mejor idea:

En sistemas GNU / Linux y GNU / Hurd, clock_t es equivalente a long int y CLOCKS_PER_SEC es un valor entero. Pero en otros sistemas, tanto clock_t como la macro CLOCKS_PER_SEC pueden ser de tipo entero o coma flotante. Al duplicar los valores de tiempo de la CPU, como en el ejemplo anterior, se asegura de que las operaciones como la aritmética y la impresión funcionen correcta y consistentemente, sin importar cuál sea la representación subyacente.

En glibc 2.21:

Ver también


Probablemente sea porque las marcas de los relojes no son una unidad muy bien definida. Puede convertirlo a 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 se expande a una expresión que representa la cantidad de marcas de reloj en un segundo.


Una forma es usar la función gettimeofday . Uno puede encontrar la diferencia usando 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; }