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

StackOverflow https://stackoverflow.com/questions/1083142

  •  23-08-2019
  •  | 
  •  

Вопрос

В настоящее время я использую явное приведение к 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 является ли в настоящее время максимально возможным стандартным целочисленным типом:

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

#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 может быть реальным типом:

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;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top