Hello,
I imagine this has been discussed but cannot find any traces of it.
The standard mandates that duration_cast returns
ToDuration(static_cast<typename ToDuration::rep>(static_cast<CR>(d.count()) * static_cast<CR>(CF::num) / static_cast<CR>(CF::den)))
when the conversion ration has non 1 denominator and numerator (case 2.4 of 23.17.5.7).
For integer representations this has a good probability of int overflow even when the result is within the representation range.
One example:
int main(){
using namespace std; using namespace std::chrono;
//fixed point 32 bit seconds 32 bit second fractions
using D = duration<int64_t, ratio<1, 1L<<32 > >;
seconds s{1490000000}; //fits in 32 bit int
cout << duration_cast<D>(s).count() << '\n';
cout << duration_cast<D>(milliseconds{s}).count() << '\n';
cout << duration_cast<D>(microseconds{s}).count() << '\n';
cout << duration_cast<D>(nanoseconds{s}).count() << '\n';
}
outputs :
6399501271040000000
53821309683914244
-485904869086675
-4223487833972
and only the first result is correct
an alternative for int representation would be to request case 2.4 to do
constexpr auto q = static_cast<CR>(CF::num) / static_cast<CR>(CF::den); //may be compile time 0
constexpr auto r = static_cast<CR>(CF::num) % static_cast<CR>(CF::den);
return ToDuration( static_cast<TR>( static_cast<CR>(d.count()) * q +
static_cast<CR>(d.count()) / static_cast<CR>(CF::den) * r +
static_cast<CR>(s.count()) % static_cast<CR>(CF::den) * r / static_cast<CR>(CF::den)));
If the end result can be represented with CR this cannot overflow if static_cast<CR>(CF::num) % static_cast<CR>(CF::den) * (static_cast<CR>(CF::num) % static_cast<CR>(CF::den) - 1)
does not overflow.
using namespace std;
using namespace std::chrono;
template<typename ToDuration, typename SR, typename SP>
constexpr ToDuration example_cast(const duration<SR, SP>& d) {
typedef typename ToDuration::period TP;
typedef typename ToDuration::rep TR;
typedef ratio_divide<SP, TP> CF;
typedef typename common_type<TR, SR, intmax_t>::type CR;
constexpr auto q = static_cast<CR>(CF::num) / static_cast<CR>(CF::den);
constexpr auto r = static_cast<CR>(CF::num) % static_cast<CR>(CF::den);
return ToDuration( static_cast<TR>( static_cast<CR>(d.count()) * q +
static_cast<CR>(d.count()) / static_cast<CR>(CF::den) * r +
static_cast<CR>(d.count()) % static_cast<CR>(CF::den) * r / static_cast<CR>(CF::den)));
}
int main(){
using D = duration<int64_t, ratio<1, 1ull<<32 > >;
seconds since_epoch{1490000000};
cout << example_cast<D>(since_epoch).count() << '\n';
cout << example_cast<D>(milliseconds{since_epoch}).count() << '\n';
cout << example_cast<D>(microseconds{since_epoch}).count() << '\n';
cout << example_cast<D>(nanoseconds{since_epoch}).count() << '\n';
cout << example_cast<seconds>(example_cast<D>(since_epoch)).count() << '\n';
cout << example_cast<milliseconds>(example_cast<D>(milliseconds{since_epoch})).count() << '\n';
cout << example_cast<microseconds>(example_cast<D>(microseconds{since_epoch})).count() << '\n';
cout << example_cast<nanoseconds>(example_cast<D>(nanoseconds{since_epoch})).count() << '\n';
}
outputs:
6399501271040000000
6399501271040000000
6399501271040000000
6399501271040000000
1490000000
1490000000000
1490000000000000
1490000000000000000
Is this a valid issue with the duration_cast specification?