Howard Hinnant
2010-10-30

<chrono> Utilities

A few simple utility functions for working with durations.

Rounding functions

// round down
template <class To, class Rep, class Period>
To
floor(const std::chrono::duration<Rep, Period>& d)
{
    To t = std::chrono::duration_cast<To>(d);
    if (t > d)
        --t;
    return t;
}

// round to nearest, to even on tie
template <class To, class Rep, class Period>
To
round(const std::chrono::duration<Rep, Period>& d)
{
    To t0 = std::chrono::duration_cast<To>(d);
    To t1 = t0;
    ++t1;
    auto diff0 = d - t0;
    auto diff1 = t1 - d;
    if (diff0 == diff1)
    {
        if (t0.count() & 1)
            return t1;
        return t0;
    }
    else if (diff0 < diff1)
        return t0;
    return t1;
}

// round up
template <class To, class Rep, class Period>
To
ceil(const std::chrono::duration<Rep, Period>& d)
{
    To t = std::chrono::duration_cast<To>(d);
    if (t < d)
        ++t;
    return t;
}

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.

Example use of rounding functions

#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::chrono::duration<Rep, Period>::min() <
              std::chrono::duration<Rep, Period>::zero()
          >::type
         >
constexpr
inline
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.