<chrono>
Utilities
A few simple utility functions for working with duration
s.
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.