2

I'm testing a timer based on the ctime library using the clock() function. Please note that the code that follows is only for test purposes.

#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;
}

As you can see I'm performing an implicit conversion from double to unsigned long when Elapsed() returns the calculated value. The unsigned long limit for a 32 bit system is 2,147,483,647 and I get overflow after Elapsed() returns 2146.

Looks like the function converts "ticks" to unsigned long, CLOCK_PER_SEC to unsigned long and then it returns the value. When it converts the "ticks" it overflows.

I expected it, instead, to first calculate the value in double of "ticks"/CLOCK_PER_SEC and THEN convert it to unsigned long.

In the attempt to count more seconds I tried to return a unsigned long long data type, but the variable always overflows at the same value (2147).

Could you explain me why the compiler converts to unsigned long long "a priori" and why even with unsigned long long it overflows at the same value ? Is there any way to write the Elapsed() function in a better way to prevent the overflow to happen ?

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
Podarce
  • 473
  • 1
  • 6
  • 22
  • 2
    The limit for `unsigned long` for 32bit systems is `4294967295`, it is `std::clock()`, which wraps around every 2147 seconds: https://en.cppreference.com/w/cpp/chrono/c/clock So `seconds` is a value between `0` and `2147`, so the implicit conversion to `unsigned long` is fine. – mch Jul 02 '18 at 08:32
  • @M.M Edited, sorry. – Podarce Jul 02 '18 at 09:14

3 Answers3

5

Contrary to popular belief, the behaviour on converting a floating point type such as a double to any integral type is undefined if the value cannot fit into that integral type.

So introducing a double in your function is a poor thing to do indeed.

Why not write return ticks / CLOCKS_PER_SEC; instead if you can allow for truncation and wrap-around effects? Or if not, use a unsigned long long as the return value.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
3

If on your system, clock_t is a 32 bit type, then it's likely it'll wrap around after 2147 seconds like you're seeing. This is expected behavior (ref. clock). And no amount of casting will get around that. Your code needs to be able to deal with the wrap-around (either by ignoring it, or by explicitly accounting for it).

Sander De Dycker
  • 16,053
  • 1
  • 35
  • 40
3

When it converts the "ticks" it overflows.

No, the clock itself "overflows"; the conversion has nothing to do with it. That said, the conversion to double is pointless. Your limitation is the type clock_t. See notes for example from this reference:

The value returned by clock() may wrap around on some implementations. For example, on a machine with 32-bit clock_t, it wraps after 2147 seconds or 36 minutes.

One alternative, if it's available to you, is to rely on POSIX standard instead of C standard library. It provides clock_gettime which can be used to get the CPU time represented in timespec. Not only does it not suffer from this overlflow (until much longer timespan), but it also may have higher resolution than clock. The linked reference page of clock() conveniently shows example usage of clock_gettime as well.

eerorika
  • 232,697
  • 12
  • 197
  • 326