Неявное преобразование double в unsigned long overflow c ++ - PullRequest
0 голосов
/ 02 июля 2018

Я тестирую таймер на основе библиотеки ctime с помощью функции clock (). Обратите внимание, что следующий код предназначен только для целей тестирования.

#include <ctime>

unsigned long Elapsed(void);

clock_t start = 0;
clock_t stop = 0;

int main()
{
  start = std::clock();
  while(1)
   {
    sleep(1);
    cout << "Elapsed seconds: " << Elapsed() << endl;
   }
return 0;
}

unsigned long Elapsed()
{
  stop = std::clock();
  clock_t ticks = stop - start;
  double seconds = (double)ticks / CLOCKS_PER_SEC;  //CLOCK_PER_SEC = 1 milion
  return seconds;
}

Как вы можете видеть, я выполняю неявное преобразование из double в unsigned long, когда Elapsed () возвращает вычисленное значение. Длинный предел без знака для 32-битной системы составляет 2 147 483 647, и я получаю переполнение после того, как Elapsed () вернет 2146.

Похоже, что функция преобразует "тики" в unsigned long, CLOCK_PER_SEC в unsigned long, а затем возвращает значение. Когда он преобразует «тики», он переполняется.

Я ожидал, что вместо этого сначала вычислю значение в двойном значении "ticks" / CLOCK_PER_SEC, а затем преобразую его в unsigned long.

В попытке подсчитать больше секунд я пытался вернуть тип данных unsigned long long, но переменная всегда переполняется одним и тем же значением (2147).

Не могли бы вы объяснить, почему компилятор конвертирует в unsigned long long "a priori" и почему даже при unsigned long long он переполняется при одном и том же значении? Есть ли способ написать функцию Elapsed () лучше, чтобы предотвратить переполнение?

Ответы [ 3 ]

0 голосов
/ 02 июля 2018

Если в вашей системе clock_t является 32-битным типом, то, скорее всего, он будет повторяться через 2147 секунд, как вы видите. Это ожидаемое поведение (ref. clock). И никакое количество кастинга не обойдется. Ваш код должен иметь возможность справляться с циклическим изменением (либо игнорируя его, либо явно учитывая его).

0 голосов
/ 02 июля 2018

Когда он преобразует «тики», он переполняется.

Нет, сами часы "переполняются"; преобразование не имеет к этому никакого отношения. Тем не менее, преобразование в double бессмысленно. Ваше ограничение типа clock_t. См. Примечания, например, из этой ссылки :

Значение, возвращаемое функцией clock (), может быть изменено в некоторых реализациях. Например, на компьютере с 32-разрядным clock_t оно переносится через 2147 секунд или 36 минут.

Один из вариантов, если он вам доступен, - это полагаться на стандарт POSIX вместо стандартной библиотеки C. Он предоставляет clock_gettime, который можно использовать для получения времени ЦП, представленного в timespec. Он не только не страдает от этого переполнения (до гораздо более длительного промежутка времени), но также может иметь более высокое разрешение, чем clock. Ссылочная страница ссылки clock() также показывает пример использования clock_gettime.

0 голосов
/ 02 июля 2018

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

Таким образом, введение double в вашу функцию - плохая вещь.

Почему бы не написать return ticks / CLOCKS_PER_SEC; вместо этого, если вы можете разрешить эффекты усечения и обтекания? Или, если нет, используйте unsigned long long в качестве возвращаемого значения.

...