<chrono> Utilities
A few simple utility functions for working with durations.
namespace detail {
template <class T>
struct choose_trunc_type
{
static const int digits = std::numeric_limits<T>::digits;
using type = typename std::conditional
<
digits < 32,
std::int32_t,
typename std::conditional
<
digits < 64,
std::int64_t,
#ifdef __SIZEOF_INT128__
__int128
#else
std::int64_t
#endif
>::type
>::type;
};
template <class T>
constexpr
inline
typename std::enable_if
<
!std::chrono::treat_as_floating_point<T>::value,
T
>::type
trunc(T t) NOEXCEPT
{
return t;
}
template <class T>
constexpr
inline
typename std::enable_if
<
std::chrono::treat_as_floating_point<T>::value,
T
>::type
trunc(T t) NOEXCEPT
{
using namespace std;
using I = typename choose_trunc_type<T>::type;
constexpr auto digits = numeric_limits<T>::digits;
static_assert(digits < numeric_limits<I>::digits, "");
constexpr auto max = I{1} << (digits-1);
constexpr auto min = -max;
const auto negative = t < T{0};
if (min <= t && t <= max && t != 0 && t == t)
{
t = static_cast<T>(static_cast<I>(t));
if (t == 0 && negative)
t = -t;
}
return t;
}
} // detail
// trunc towards zero
template <class To, class Rep, class Period>
constexpr
inline
To
trunc(const std::chrono::duration<Rep, Period>& d)
{
return To{detail::trunc(std::chrono::duration_cast<To>(d).count())};
}
// round down
template <class To, class Rep, class Period>
constexpr
inline
To
floor(const std::chrono::duration<Rep, Period>& d)
{
auto t = trunc<To>(d);
if (t > d)
return t - To{1};
return t;
}
// round to nearest, to even on tie
template <class To, class Rep, class Period>
constexpr
inline
To
round(const std::chrono::duration<Rep, Period>& d)
{
auto t0 = floor<To>(d);
auto t1 = t0 + To{1};
if (t1 == To{0} && t0 < To{0})
t1 = -t1;
auto diff0 = d - t0;
auto diff1 = t1 - d;
if (diff0 == diff1)
{
if (t0 - trunc<To>(t0/2)*2 == To{0})
return t0;
return t1;
}
if (diff0 < diff1)
return t0;
return t1;
}
// round up
template <class To, class Rep, class Period>
constexpr
inline
To
ceil(const std::chrono::duration<Rep, Period>& d)
{
auto t = trunc<To>(d);
if (t < d)
return t + To{1};
return t;
}
// trunc towards zero
template <class To, class Clock, class FromDuration>
constexpr
inline
std::chrono::time_point<Clock, To>
trunc(const std::chrono::time_point<Clock, FromDuration>& tp)
{
using std::chrono::time_point;
return time_point<Clock, To>{trunc<To>(tp.time_since_epoch())};
}
// round down
template <class To, class Clock, class FromDuration>
constexpr
inline
std::chrono::time_point<Clock, To>
floor(const std::chrono::time_point<Clock, FromDuration>& tp)
{
using std::chrono::time_point;
return time_point<Clock, To>{floor<To>(tp.time_since_epoch())};
}
// round to nearest, to even on tie
template <class To, class Clock, class FromDuration>
constexpr
inline
std::chrono::time_point<Clock, To>
round(const std::chrono::time_point<Clock, FromDuration>& tp)
{
using std::chrono::time_point;
return time_point<Clock, To>{round<To>(tp.time_since_epoch())};
}
// round up
template <class To, class Clock, class FromDuration>
constexpr
inline
std::chrono::time_point<Clock, To>
ceil(const std::chrono::time_point<Clock, FromDuration>& tp)
{
using std::chrono::time_point;
return time_point<Clock, To>{ceil<To>(tp.time_since_epoch())};
}
The beauty of the chrono library is the ease and accuracy with which such conversions can be made. For example to convert from milliseconds (1/1000 of a second), to 1/30 of a second, one must multiply the milliseconds by 0.03. It is common knowledge that you can't exactly represent 0.03 in a computer. Nevertheless round will exactly (with no round off error) detect a tie and round to even when this happens. The differences diff0 and diff1 are not approximate, but exact differences, even when d has the units of millisecond and To is 1/30 of a second. The unit of diff0 and diff1 is 1/3000 of a second which both millisecond and 1/30 of a second exactly convert to (with no truncation error).
Similarly, the comparison t < d in ceil is exact, even when there is no exact conversion between t and d.
#include <iostream>
#include <chrono_io>
int main()
{
using namespace std::chrono;
milliseconds ms(2500);
std::cout << floor<seconds>(ms) << '\n';
std::cout << round<seconds>(ms) << '\n';
std::cout << ceil<seconds>(ms) << '\n';
ms = milliseconds(2516);
typedef duration<long, std::ratio<1, 30>> frame_rate;
std::cout << floor<frame_rate>(ms) << '\n';
std::cout << round<frame_rate>(ms) << '\n';
std::cout << ceil<frame_rate>(ms) << '\n';
}
2 seconds
2 seconds
3 seconds
75 [1/30]seconds
75 [1/30]seconds
76 [1/30]seconds
An interesting exercise is to find out how many frames go by (at 1/30 second frame rate) in 2516 milliseconds using xtime from C1x (without using the chrono library! :-)).
abs
template <class Rep, class Period,
class = typename std::enable_if
<
std::numeric_limits<Rep>::is_signed
>::type>
CONSTCD11
std::chrono::duration<Rep, Period>
abs(std::chrono::duration<Rep, Period> d)
{
return d >= d.zero() ? d : -d;
}
Note that abs is disabled for unsigned durations. My reasoning is
that if you try to call abs with an unsigned duration, the chances
are high that there is a logical mistake in your code, and so a compile-time
diagnostic is better, than a silent identity function.