Qual é a maneira correta de usar printf para imprimir um clock_t?
Pergunta
Atualmente estou usando um elenco explícito de unsigned long long
e usando %llu
imprimi-lo, mas desde size_t
tem o especificador %z
, por que não clock_t
tem um?
Não há sequer uma macro para isso. Talvez eu possa supor que em um sistema x64 (OS e CPU) size_t
é de 8 bytes de comprimento (e mesmo neste caso, eles têm fornecido %z
), mas que sobre clock_t
?
Solução
Não parece haver nenhuma maneira perfeita. A raiz do problema é que clock_t
pode ser inteiro ou de vírgula flutuante.
clock_t pode ser um tipo de ponto flutuante
Como Bastien Léonard menciona para POSIX (ir upvote-lo), C99 N1256 projecto 7.23.1 / 3 também diz que:
[clock_t é] tipos aritméticos capazes de representar vezes
e 6.2.5 / 18:
Integer e tipos flutuantes são chamados coletivamente de tipos aritméticos.
e o padrão define tipo aritmética como quer inteiros ou flutuantes tipos de pontos.
Se você dividir por CLOCKS_PER_SEC, uso a longo dupla
O valor de retorno de clock()
é implementação definida, ea única maneira de obter significado padrão de fora, é para dividir por CLOCKS_PER_SEC
para encontrar o número de segundos:
clock_t t0 = clock();
/* Work. */
clock_t t1 = clock();
printf("%Lf", (long double)(t1 - t0));
Isso é bom o suficiente, embora não seja perfeito, pelas duas razões seguintes:
-
parece haver nenhum análogo ao
intmax_t
para tipos de ponto flutuante: Como obter a maior precisão flutuante tipo de dados ponto de implemenation e sua printf especificador? Então, se um tipo de ponto flutuante maior sai amanhã, poderia ser usado e quebrar a sua implementação. -
Se
clock_t
é um inteiro, o elenco de flutuador é bem definido para usar o flutuador mais próximo possível. Você pode perder precisão, mas não importa muito em comparação com o valor absoluto, e só iria acontecer por enormes quantidades de tempo, por exemplo,long int
em x86 é a bóia 80-bit com 64-bit significativa, que é milhões de anos em segundos.
Go upvote lemonad que disse algo similar.
Se você acha que ele é um inteiro, uso% ju e uintmax_t
Embora unsigned long long
é atualmente o maior tipo inteiro padrão possível:
- um maior poderia sair no futuro
- o padrão já permite explicitamente maior implementação definido tipos (parabéns a @FUZxxl) e
clock_t
poderia ser um deles
por isso é melhor para distribuir os papéis para o maior tipo inteiro sem sinal possível:
#include <stdint.h>
printf("%ju", (uintmax_t)(clock_t)1);
uintmax_t
está garantido para ter o tamanho do maior tamanho inteiro possível na máquina.
uintmax_t
e sua %ju
printf especificador foram introduzidos em c99 e gcc, por exemplo, implementa-las.
Como um bônus, isso resolve uma vez por todas a questão de como confiável printf
inteiro tipos (que infelizmente não é o necessariamente o caso para clock_t
).
O que poderia dar errado se foi um duplo:
- se for demasiado grande para caber em um inteiro, um comportamento indefinido
- muito menor do que 1, vai ser arredondado para 0 e você não vai ver nada
Uma vez que essas conseqüências são muito mais duras do que o inteiro para conversão de float, usando float é provavelmente uma idéia melhor.
Em glibc 2.21 é um inteiro
O manual diz que o uso de double
é uma idéia melhor:
sistemas em GNU / Linux e GNU / Hurd, clock_t é equivalente a long int e CLOCKS_PER_SEC é um valor inteiro. Mas em outros sistemas, tanto clock_t ea macro CLOCKS_PER_SEC podem ser tanto inteiros ou de ponto flutuante tipos. Fundição valores de tempo de CPU para o dobro, como no exemplo acima, torna-se de que as operações tais como o trabalho aritmética e imprimindo corretamente e consiste dontly Não importa o que a representação subjacente é.
Em glibc 2.21:
-
clock_t
élong int
:- tempo / time.h sets-lo para
__clock_t
- bits / types.h sets-lo para
__CLOCK_T_TYPE
- bits / typesizes.h sets-lo para
__SLONGWORD_TYPE
- bits / types.h sets-lo para
long int
- tempo / time.h sets-lo para
-
clock()
em Linux é implementado comsys_clock_gettime
:- sysdeps / unix / sysv / linux / clock.c chama
__clock_gettime
- sysdeps / unix / clock_gettime.c chama
SYSDEP_GETTIME_CPU
- sysdeps / unix / sysv / linux / clock_gettime.c chama
SYSCALL_GETTIME
que finalmente faz uma chamada de sistema em linha
man clock_gettime
, nos diz que ele retorna umstruct timespec
que no GCC contém camposlong int
.Assim, a implementação subjacente realmente retorna inteiros.
- sysdeps / unix / sysv / linux / clock.c chama
Veja também
Outras dicas
Tanto quanto eu sei, a maneira que você está fazendo é o melhor. Só que clock_t
pode ser um tipo real:
time_t
eclock_t
será inteiro ou tipos flutuante real.
http://www.opengroup.org/onlinepubs/ 009695399 / basedefs / sys / types.h.html
É provavelmente porque tiques do relógio não é uma unidade muito bem definido. Você pode convertê-lo em segundos e imprimi-lo como um duplo:
time_in_seconds = (double)time_in_clock_ticks / (double)CLOCKS_PER_SEC;
printf("%g seconds", seconds);
Os CLOCKS_PER_SEC expande macro para uma expressão representando o número de tiques do relógio em um segundo.
O padrão C tem para acomodar uma grande variedade de arquiteturas, o que torna impossível fazer quaisquer outras garantias além do fato de que o tipo de relógio interno é aritmética.
Na maioria dos casos, você está interessado em intervalos de tempo, então eu converter a diferença de pulsos de clock para milissegundos. Um unsigned long
é grande o suficiente para representar um intervalo de cerca de 50 dias, mesmo se o seu 32 bits, por isso deve ser grande o suficiente para a maioria dos casos:
clock_t start;
clock_t end;
unsigned long millis = (end - start) * 1000 / CLOCKS_PER_SEC;
Uma maneira é usando a função gettimeofday
. Pode-se encontrar a diferença usando esta função:
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;
}