From e08458e2799506050b733e0bd18aeb15ce484b69 Mon Sep 17 00:00:00 2001 From: Howard Hinnant Date: Sun, 22 Nov 2015 21:57:49 -0500 Subject: [PATCH] Update rounding utilities. --- date.h | 127 +++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 101 insertions(+), 26 deletions(-) diff --git a/date.h b/date.h index fc02e15..c9eda8a 100644 --- a/date.h +++ b/date.h @@ -796,14 +796,76 @@ public: {} }; -// truncate towards zero +namespace detail +{ + +template +struct choose_trunc_type +{ + static const int digits = std::numeric_limits::digits; + using type = typename std::conditional + < + digits < 32, + std::int32_t, + typename std::conditional + < + digits < 64, + std::int64_t, +#ifndef _MSC_VER + __int128 +#else + std::int64_t +#endif + >::type + >::type; +}; + +template +CONSTCD11 +inline +typename std::enable_if +< + !std::chrono::treat_as_floating_point::value, + T +>::type +trunc(T t) noexcept +{ + return t; +} + +template +CONSTCD14 +inline +typename std::enable_if +< + std::chrono::treat_as_floating_point::value, + T +>::type +trunc(T t) noexcept +{ + using namespace std; + using I = typename choose_trunc_type::type; + CONSTDATA auto digits = numeric_limits::digits; +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) + static_assert(digits < numeric_limits::digits, ""); +#endif + CONSTDATA auto max = I{1} << (digits-1); + CONSTDATA auto min = -max; + if (min <= t && t <= max && t != 0 && t == t) + t = static_cast(static_cast(t)); + return t; +} + +} // detail + +// trunc towards zero template CONSTCD11 inline To -truncate(const std::chrono::duration& d) +trunc(const std::chrono::duration& d) { - return std::chrono::duration_cast(d); + return To{detail::trunc(std::chrono::duration_cast(d).count())}; } // round down @@ -813,9 +875,9 @@ inline To floor(const std::chrono::duration& d) { - To t = std::chrono::duration_cast(d); + auto t = trunc(d); if (t > d) - t = t - To{1}; + return t - To{1}; return t; } @@ -826,17 +888,17 @@ inline To round(const std::chrono::duration& d) { - To t0 = floor(d); - To t1 = t0 + To{1}; + auto t0 = floor(d); + auto t1 = t0 + To{1}; auto diff0 = d - t0; auto diff1 = t1 - d; if (diff0 == diff1) { - if (t0.count() & 1) - return t1; - return t0; + if (t0 - trunc(t0/2)*2 == To{0}) + return t0; + return t1; } - else if (diff0 < diff1) + if (diff0 < diff1) return t0; return t1; } @@ -848,20 +910,33 @@ inline To ceil(const std::chrono::duration& d) { - To t = std::chrono::duration_cast(d); + auto t = trunc(d); if (t < d) - t = t + To{1}; + return t + To{1}; return t; } -// truncate towards zero +template ::is_signed + >::type> +CONSTCD11 +std::chrono::duration +abs(std::chrono::duration d) +{ + return d >= d.zero() ? d : -d; +} + +// trunc towards zero template CONSTCD11 inline std::chrono::time_point -truncate(const std::chrono::time_point& tp) +trunc(const std::chrono::time_point& tp) { - return std::chrono::time_point_cast(tp); + using std::chrono::time_point; + return time_point{trunc(tp.time_since_epoch())}; } // round down @@ -3278,9 +3353,9 @@ public: os.flags(std::ios::dec | std::ios::right); if (t.mode_ != am && t.mode_ != pm) os.width(2); - os << abs(t.h_.count()) << ':'; + os << std::abs(t.h_.count()) << ':'; os.width(2); - os << abs(t.m_.count()); + os << std::abs(t.m_.count()); switch (t.mode_) { case am: @@ -3349,11 +3424,11 @@ public: os.flags(std::ios::dec | std::ios::right); if (t.mode_ != am && t.mode_ != pm) os.width(2); - os << abs(t.h_.count()) << ':'; + os << std::abs(t.h_.count()) << ':'; os.width(2); - os << abs(t.m_.count()) << ':'; + os << std::abs(t.m_.count()) << ':'; os.width(2); - os << abs(t.s_.count()); + os << std::abs(t.s_.count()); switch (t.mode_) { case am: @@ -3429,22 +3504,22 @@ public: os.flags(std::ios::dec | std::ios::right); if (t.mode_ != am && t.mode_ != pm) os.width(2); - os << abs(t.h_.count()) << ':'; + os << std::abs(t.h_.count()) << ':'; os.width(2); - os << abs(t.m_.count()) << ':'; + os << std::abs(t.m_.count()) << ':'; os.width(2); - os << abs(t.s_.count()) << '.'; + os << std::abs(t.s_.count()) << '.'; #if __cplusplus >= 201402 CONSTDATA auto cl10 = ceil_log10(Period::den); using scale = std::ratio_multiply>; os.width(cl10); - os << abs(t.sub_s_.count()) * scale::num / scale::den; + os << std::abs(t.sub_s_.count()) * scale::num / scale::den; #else // __cplusplus >= 201402 // inefficient sub-optimal run-time mess, but gets the job done const unsigned long long cl10 = std::ceil(log10(Period::den)); const auto p10 = std::pow(10., cl10); os.width(cl10); - os << static_cast(abs(t.sub_s_.count()) + os << static_cast(std::abs(t.sub_s_.count()) * Period::num * p10 / Period::den); #endif // __cplusplus >= 201402 switch (t.mode_)