Каков правильный способ использования printf для печати clock_t?

В настоящее время я использую явное преобразование в unsigned long long и используя %llu для его печати, но поскольку size_t имеет спецификатор %z, почему clock_t не имеет одного?

Для этого нет даже макроса. Возможно, я могу предположить, что в x64-системе (ОС и ЦП) size_t длина 8 байтов (и даже в этом случае они предоставили %z), но как насчет clock_t?

Ответ 1

Кажется, нет идеального пути. Корень проблемы состоит в том, что clock_t может быть целым или плавающим.

clock_t может быть типом с плавающей точкой

Как Bastien Léonard упоминает для POSIX (перейдите вверх), C99 N1256 draft 7.23.1/3 также говорит, что:

[clock_t is] арифметические типы, способные представлять время

и 6.2.5/18:

Целочисленные и плавающие типы совместно называются арифметическими типами.

а стандарт определяет арифметический тип как целые числа, так и типы с плавающей запятой.

Если вы разделите CLOCKS_PER_SEC, используйте длинный двойной

Возвращаемое значение clock() определяется реализацией, и единственный способ получить стандартное значение из него состоит в том, чтобы разделить на CLOCKS_PER_SEC, чтобы найти количество секунд:

clock_t t0 = clock();
/* Work. */
clock_t t1 = clock();
printf("%Lf", (long double)(t1 - t0));

Это достаточно хорошо, хотя и не идеально, по двум следующим причинам:

  • для типов с плавающей точкой не существует аналога intmax_t: Как получить максимальный тип данных с плавающей запятой и его спецификатор printf? Так, если завтра появится более крупный тип с плавающей точкой, его можно будет использовать и прервать вашу реализацию.

  • если clock_t является целым числом, метод cast to float корректно определен, чтобы использовать ближайший поплавок. Вы можете потерять точность, но это не имеет большого значения по сравнению с абсолютной величиной и будет происходить только в течение огромного количества времени, например. long int в x86 - это 80-битный поплавок с 64-битным значащим значением, который составляет миллионы лет в секундах.

Перейдите в лимонад, который сказал что-то подобное.

Если вы считаете, что это целое число, используйте% ju и uintmax_t

Хотя unsigned long long в настоящее время является наибольшим стандартным целочисленным типом:

поэтому лучше всего прибегнуть к максимальному максимальному целочисленному типу без знака:

#include <stdint.h>

printf("%ju", (uintmax_t)(clock_t)1);

uintmax_t гарантированно имеет размер наибольшего возможного целочисленного размера на машине.

uintmax_t и его спецификатор printf %ju были введены в c99, и gcc, например, реализует их.

В качестве бонуса это решает раз и навсегда вопрос о том, как надежно printf целочисленные типы (что, к сожалению, не обязательно имеет место для clock_t).

Что может пойти не так, если он был двойным:

  • если слишком большой, чтобы вписаться в целое число, undefined поведение
  • намного меньше 1, округляется до 0 и вы ничего не увидите

Поскольку эти последствия намного более жесткие, чем целочисленное преобразование с плавающей точкой, использование float, вероятно, является лучшей идеей.

В glibc 2.21 это целое число

В руководстве говорится, что использование double - лучшая идея:

В системах GNU/Linux и GNU/Hurd clock_t эквивалентен long int, а CLOCKS_PER_SEC - целочисленное значение. Но в других системах, как clock_t, так и макрос CLOCKS_PER_SEC могут быть либо целыми, либо с плавающей точкой. Приведение значений времени CPU в два раза, как в приведенном выше примере, гарантирует, что операции, такие как арифметика и печать, работают правильно и последовательно независимо от того, что представляет собой основное представление.

В glibc 2.21:

  • clock_t long int:

    • time/time.h устанавливает его в __clock_t
    • bits/types.h устанавливает его в __CLOCK_T_TYPE
    • bits/typesizes.h устанавливает его на __SLONGWORD_TYPE
    • bits/types.h устанавливает его в long int
  • clock() в Linux реализована с помощью sys_clock_gettime:

    man clock_gettime, сообщает нам, что он возвращает struct timespec, который в GCC содержит поля long int.

    Итак, основная реализация действительно возвращает целые числа.

См. также

Ответ 2

Возможно, это связано с тем, что тактовая частота не является очень четкой единицей. Вы можете преобразовать его в секундах и распечатать его как double:

time_in_seconds = (double)time_in_clock_ticks / (double)CLOCKS_PER_SEC;
printf("%g seconds", seconds);

Макрос CLOCKS_PER_SEC расширяется до выражения, представляющего количество тактов синхронизации за секунду.

Ответ 3

Насколько я знаю, лучший способ - это то, как вы делаете. За исключением того, что clock_t может быть реальным типом:

time_t и clock_t должны быть целыми или реальными плавающими типами.

http://www.opengroup.org/onlinepubs/009695399/basedefs/sys/types.h.html

Ответ 4

Стандарт C должен сочетать в себе множество архитектур, что делает невозможным любые дальнейшие гарантии, кроме того, что внутренний тип часов является арифметическим.

В большинстве случаев вас интересуют временные интервалы, поэтому я бы конвертировал разницу в тактовых импульсах в миллисекундах. unsigned long достаточно большой, чтобы представлять интервал почти 50 дней, даже если его 32-разрядный, поэтому он должен быть достаточно большим для большинства случаев:

clock_t start;
clock_t end;
unsigned long millis = (end - start) * 1000 / CLOCKS_PER_SEC;

Ответ 5

Одним из способов является использование функции 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;
}