Update rounding utilities.

This commit is contained in:
Howard Hinnant
2015-11-22 21:57:49 -05:00
parent c5f307fdb8
commit e08458e279

127
date.h
View File

@@ -796,14 +796,76 @@ public:
{} {}
}; };
// truncate towards zero 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,
#ifndef _MSC_VER
__int128
#else
std::int64_t
#endif
>::type
>::type;
};
template <class T>
CONSTCD11
inline
typename std::enable_if
<
!std::chrono::treat_as_floating_point<T>::value,
T
>::type
trunc(T t) noexcept
{
return t;
}
template <class T>
CONSTCD14
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;
CONSTDATA auto digits = numeric_limits<T>::digits;
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
static_assert(digits < numeric_limits<I>::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<T>(static_cast<I>(t));
return t;
}
} // detail
// trunc towards zero
template <class To, class Rep, class Period> template <class To, class Rep, class Period>
CONSTCD11 CONSTCD11
inline inline
To To
truncate(const std::chrono::duration<Rep, Period>& d) trunc(const std::chrono::duration<Rep, Period>& d)
{ {
return std::chrono::duration_cast<To>(d); return To{detail::trunc(std::chrono::duration_cast<To>(d).count())};
} }
// round down // round down
@@ -813,9 +875,9 @@ inline
To To
floor(const std::chrono::duration<Rep, Period>& d) floor(const std::chrono::duration<Rep, Period>& d)
{ {
To t = std::chrono::duration_cast<To>(d); auto t = trunc<To>(d);
if (t > d) if (t > d)
t = t - To{1}; return t - To{1};
return t; return t;
} }
@@ -826,17 +888,17 @@ inline
To To
round(const std::chrono::duration<Rep, Period>& d) round(const std::chrono::duration<Rep, Period>& d)
{ {
To t0 = floor<To>(d); auto t0 = floor<To>(d);
To t1 = t0 + To{1}; auto t1 = t0 + To{1};
auto diff0 = d - t0; auto diff0 = d - t0;
auto diff1 = t1 - d; auto diff1 = t1 - d;
if (diff0 == diff1) if (diff0 == diff1)
{ {
if (t0.count() & 1) if (t0 - trunc<To>(t0/2)*2 == To{0})
return t1; return t0;
return t0; return t1;
} }
else if (diff0 < diff1) if (diff0 < diff1)
return t0; return t0;
return t1; return t1;
} }
@@ -848,20 +910,33 @@ inline
To To
ceil(const std::chrono::duration<Rep, Period>& d) ceil(const std::chrono::duration<Rep, Period>& d)
{ {
To t = std::chrono::duration_cast<To>(d); auto t = trunc<To>(d);
if (t < d) if (t < d)
t = t + To{1}; return t + To{1};
return t; return t;
} }
// truncate towards zero 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;
}
// trunc towards zero
template <class To, class Clock, class FromDuration> template <class To, class Clock, class FromDuration>
CONSTCD11 CONSTCD11
inline inline
std::chrono::time_point<Clock, To> std::chrono::time_point<Clock, To>
truncate(const std::chrono::time_point<Clock, FromDuration>& tp) trunc(const std::chrono::time_point<Clock, FromDuration>& tp)
{ {
return std::chrono::time_point_cast<To>(tp); using std::chrono::time_point;
return time_point<Clock, To>{trunc<To>(tp.time_since_epoch())};
} }
// round down // round down
@@ -3278,9 +3353,9 @@ public:
os.flags(std::ios::dec | std::ios::right); os.flags(std::ios::dec | std::ios::right);
if (t.mode_ != am && t.mode_ != pm) if (t.mode_ != am && t.mode_ != pm)
os.width(2); os.width(2);
os << abs(t.h_.count()) << ':'; os << std::abs(t.h_.count()) << ':';
os.width(2); os.width(2);
os << abs(t.m_.count()); os << std::abs(t.m_.count());
switch (t.mode_) switch (t.mode_)
{ {
case am: case am:
@@ -3349,11 +3424,11 @@ public:
os.flags(std::ios::dec | std::ios::right); os.flags(std::ios::dec | std::ios::right);
if (t.mode_ != am && t.mode_ != pm) if (t.mode_ != am && t.mode_ != pm)
os.width(2); os.width(2);
os << abs(t.h_.count()) << ':'; os << std::abs(t.h_.count()) << ':';
os.width(2); os.width(2);
os << abs(t.m_.count()) << ':'; os << std::abs(t.m_.count()) << ':';
os.width(2); os.width(2);
os << abs(t.s_.count()); os << std::abs(t.s_.count());
switch (t.mode_) switch (t.mode_)
{ {
case am: case am:
@@ -3429,22 +3504,22 @@ public:
os.flags(std::ios::dec | std::ios::right); os.flags(std::ios::dec | std::ios::right);
if (t.mode_ != am && t.mode_ != pm) if (t.mode_ != am && t.mode_ != pm)
os.width(2); os.width(2);
os << abs(t.h_.count()) << ':'; os << std::abs(t.h_.count()) << ':';
os.width(2); os.width(2);
os << abs(t.m_.count()) << ':'; os << std::abs(t.m_.count()) << ':';
os.width(2); os.width(2);
os << abs(t.s_.count()) << '.'; os << std::abs(t.s_.count()) << '.';
#if __cplusplus >= 201402 #if __cplusplus >= 201402
CONSTDATA auto cl10 = ceil_log10(Period::den); CONSTDATA auto cl10 = ceil_log10(Period::den);
using scale = std::ratio_multiply<Period, std::ratio<pow10(cl10)>>; using scale = std::ratio_multiply<Period, std::ratio<pow10(cl10)>>;
os.width(cl10); 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 #else // __cplusplus >= 201402
// inefficient sub-optimal run-time mess, but gets the job done // inefficient sub-optimal run-time mess, but gets the job done
const unsigned long long cl10 = std::ceil(log10(Period::den)); const unsigned long long cl10 = std::ceil(log10(Period::den));
const auto p10 = std::pow(10., cl10); const auto p10 = std::pow(10., cl10);
os.width(cl10); os.width(cl10);
os << static_cast<unsigned long long>(abs(t.sub_s_.count()) os << static_cast<unsigned long long>(std::abs(t.sub_s_.count())
* Period::num * p10 / Period::den); * Period::num * p10 / Period::den);
#endif // __cplusplus >= 201402 #endif // __cplusplus >= 201402
switch (t.mode_) switch (t.mode_)