Как правильно использовать printf для печати clock_t?
Вопрос
В настоящее время я использую явное приведение к unsigned long long
и используя %llu
чтобы распечатать его, но поскольку size_t
имеет %z
спецификатор, почему не clock_t
есть один?
Для этого даже нет макроса.Возможно, я могу предположить, что в системе x64 (ОС и процессор) size_t
имеет длину 8 байт (и даже в этом случае они предоставили %z
), но как насчет clock_t
?
Решение
Кажется, идеального способа не существует.Корень проблемы заключается в том, что clock_t
может быть как целым числом, так и с плавающей запятой.
clock_t может быть типом с плавающей запятой
Как Bastien Léonard mentions для POSIX (проголосуйте за него), Осадка C99 N1256 В 7.23.1/3 также говорится, что:
[clock_t - это] арифметические типы, способные представлять время
и 6.2.5/18:
Целочисленные и плавающие типы в совокупности называются арифметическими типами.
и стандарт определяет арифметический тип либо как целые числа, либо как типы с плавающей запятой.
Если вы будете делить на CLOCKS_PER_SEC, используйте long double
Возвращаемое значение clock()
определена ли реализация, и единственный способ извлечь из нее стандартное значение - это разделить на CLOCKS_PER_SEC
чтобы найти количество секунд:
clock_t t0 = clock();
/* Work. */
clock_t t1 = clock();
printf("%Lf", (long double)(t1 - t0));
Это достаточно хорошо, хотя и не идеально, по двум следующим причинам:
по-видимому, нет аналога
intmax_t
для типов с плавающей запятой: Как получить тип данных с плавающей запятой наибольшей точности для реализации и его спецификатор printf? Таким образом, если завтра появится более крупный тип с плавающей запятой, он может быть использован и нарушить вашу реализацию.если
clock_t
является целым числом, приведение к float четко определено для использования ближайшего возможного значения float.Вы можете потерять точность, но это не будет иметь большого значения по сравнению с абсолютным значением и будет происходить только в течение огромного промежутка времени, напримерlong int
в x86 это 80-битное значение с плавающей запятой с 64-битным значением, которое составляет миллионы лет в секундах.
Поднимите голос за лимонад который сказал нечто подобное.
Если вы предполагаете, что это целое число, используйте %ju и uintmax_t
Хотя unsigned long long
является ли в настоящее время максимально возможным стандартным целочисленным типом:
- в будущем может появиться более крупная модель
- стандарт уже явно допускает более крупные типы, определенные реализацией (хвала @FUZxxl) и
clock_t
может быть, один из них
поэтому лучше всего приводить к максимально возможному целочисленному типу без знака:
#include <stdint.h>
printf("%ju", (uintmax_t)(clock_t)1);
uintmax_t
гарантированно имеет размер максимально возможного целого размера на компьютере.
uintmax_t
и его спецификатор printf %ju
были введены в c99, и gcc, например, реализует их.
В качестве бонуса это решает раз и навсегда вопрос о том, как надежно printf
целочисленные типы (что, к сожалению, не обязательно имеет место для clock_t
).
Что могло пойти не так, если бы это был дубль:
- если слишком большой, чтобы поместиться в целое число, неопределенное поведение
- намного меньше 1, будет округлено до 0, и вы ничего не увидите
Поскольку эти последствия намного серьезнее, чем преобразование целого числа в число с плавающей точкой, использование float, вероятно, является лучшей идеей.
В glibc 2.21 это целое число
В руководстве говорится, что с помощью double
это идея получше:
В системах GNU/Linux и GNU/Hurd clock_t эквивалентно long int, а CLOCKS_PER_SEC - целочисленному значению.Но в других системах как clock_t, так и макрос CLOCKS_PER_SEC могут быть либо целочисленными, либо типами с плавающей запятой.Преобразование значений процессорного времени в double, как в примере выше, гарантирует, что такие операции, как арифметика и печать, работают правильно и согласованно независимо от того, каково базовое представление.
В glibc 2.21:
clock_t
являетсяlong int
:- время/time.h устанавливает его в
__clock_t
- биты/типы.h устанавливает его в
__CLOCK_T_TYPE
- биты/типизирует.h устанавливает его в
__SLONGWORD_TYPE
- биты/типы.h устанавливает его в
long int
- время/time.h устанавливает его в
clock()
в Linux реализован сsys_clock_gettime
:- sysdeps/unix/sysv/linux/clock.c звонки
__clock_gettime
- системные настройки/unix/clock_gettime.c звонки
SYSDEP_GETTIME_CPU
- sysdeps/unix/sysv/linux/clock_gettime.c звонки
SYSCALL_GETTIME
который, наконец, выполняет встроенный системный вызов
man clock_gettime
, сообщает нам , что он возвращаетstruct timespec
который в GCC содержитlong int
поля.Таким образом, базовая реализация действительно возвращает целые числа.
- sysdeps/unix/sysv/linux/clock.c звонки
Смотрите также
Другие советы
Насколько я знаю, то, как ты это делаешь, - самое лучшее.За исключением этого clock_t
может быть реальным типом:
time_t
иclock_t
должны быть целочисленными или вещественными плавающими типами.
http://www.opengroup.org/onlinepubs/009695399/basedefs/sys/types.h.html
Вероятно, это потому, что тиканье часов - не очень четко определенная единица измерения.Вы можете преобразовать его в секунды и распечатать как двойной:
time_in_seconds = (double)time_in_clock_ticks / (double)CLOCKS_PER_SEC;
printf("%g seconds", seconds);
Макрос CLOCKS_PER_SEC расширяется до выражения, представляющего количество тактов в секунду.
Стандарт C должен соответствовать широкому разнообразию архитектур, что делает невозможным предоставление каких-либо дополнительных гарантий, кроме того факта, что внутренний тип синхронизации является арифметическим.
В большинстве случаев вас интересуют временные интервалы, поэтому я бы преобразовал разницу в тактах часов в миллисекунды.Ан unsigned long
достаточно велик, чтобы представлять интервал почти в 50 дней, даже если он 32-битный, поэтому он должен быть достаточно большим для большинства случаев:
clock_t start;
clock_t end;
unsigned long millis = (end - start) * 1000 / CLOCKS_PER_SEC;
Один из способов заключается в использовании gettimeofday
функция.Можно найти разницу, используя эту функцию:
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;
}