Search code examples
c++c++14c++-chrono

How to get the average of several chrono::time_points


The formula for getting the average of several numbers is of course well known:

And this formula can easily be used to get the average of chrono::durations:

template <class Rep0, class Period0>
auto
sum(const std::chrono::duration<Rep0, Period0>& d0)
{
    return d0;
}

template <class Rep0, class Period0, class ...Rep, class ...Period>
auto
sum(const std::chrono::duration<Rep0, Period0>& d0,
    const std::chrono::duration<Rep, Period>& ...d)
{
    return d0 + sum(d...);
}

template <class ...Rep, class ...Period>
auto
avg(const std::chrono::duration<Rep, Period>& ...d)
{
    return sum(d...) / static_cast<std::common_type_t<Rep...>>(sizeof...(d));
}

But chrono::time_points can't be added to one another. How can I average time_points?


Solution

  • It helps to start out with the assumption that you can add time_points, and then manipulate the formula to eliminate those additions:

    First separate out t1 from the sum:

    Next both add and subtract t1:

    Factor and rearrange:

    And then, since you are subtracting t1 the same number of times as you are summing, you can include that into the sum:

    Now you are summing durations instead of time_points! As we already have a function to sum durations, averaging time_points can easily build on that:

    template <class Clock, class Duration0, class ...Duration>
    auto
    avg(const std::chrono::time_point<Clock, Duration0>& t0,
        const std::chrono::time_point<Clock, Duration>& ...t)
    {
        constexpr auto N =
            static_cast<std::common_type_t<typename Duration0::rep,
                                           typename Duration::rep...>>(1 + sizeof...(t));
        return t0 + sum((t - t0)...) / N;
    }
    

    Averaging a single time_point is a special case since sum of durations does not handle summing zero durations (what would be the units?). So an overload for that case is easily added:

    template <class Clock, class Duration0>
    auto
    avg(const std::chrono::time_point<Clock, Duration0>& t0)
    {
        return t0;
    }
    

    This is written in C++14. It can be coded in C++11, using the trailing return type declaration syntax, which is more verbose, but completely doable.