chrono::duration_cast
s from smaller durations to larger durations do not overflowSection: 30.2 [time.syn] Status: New Submitter: Andy Giese Opened: 2016-02-05 Last modified: 2016-05-08
Priority: 4
View all other issues in [time.syn].
View all issues with New status.
Discussion:
Currently 30.2 [time.syn] states
// convenience typedefs typedef duration<signed integer type of at least 64 bits, nano> nanoseconds; typedef duration<signed integer type of at least 55 bits, micro> microseconds; typedef duration<signed integer type of at least 45 bits, milli> milliseconds; typedef duration<signed integer type of at least 35 bits > seconds; typedef duration<signed integer type of at least 29 bits, ratio< 60>> minutes; typedef duration<signed integer type of at least 23 bits, ratio<3600>> hours;
However, a duration_cast<minutes>(seconds::max())
would cause overflow if the underlying signed integers
only met the minimums specified.
duration_cast
from any smaller duration in
these "convenience typedefs" will not overflow any larger duration. That is, hours
should be able to hold
the maximum of minutes
, which should be able to hold the maximum of seconds
and so on.
More formally, if the ratio typedef A
and typedef B
is 1:Y
where Y > 1
(e.g.,
1 : 60 in case of minutes
: seconds
), then #bitsA-1
must be at least
ceil(log2(2#bitsB-1)/Y))
.
In the case of minutes
: seconds
, X = 1
, Y = 60
. Let
#bitsseconds = 32
. Therefore:
2(#bitsseconds - 1) = 231 = 2147483648
ceil(log2(231 / 60) = 26
#bitsminutes - 1 = 26
#bitsminutes = 27
Therefore, a minimum of 27 bits would be needed to store minutes
if 32 were used to store seconds
.
// convenience typedefs typedef duration<signed integer type of at least 64 bits, nano> nanoseconds; typedef duration<signed integer type of at least 55 bits, micro> microseconds; typedef duration<signed integer type of at least 46 bits, milli> milliseconds; typedef duration<signed integer type of at least 37 bits > seconds; typedef duration<signed integer type of at least 32 bits, ratio< 60>> minutes; typedef duration<signed integer type of at least 27 bits, ratio<3600>> hours;
These bits were chosen to satisfy the above formula. Note that
minimums only increased, so larger ranges could be held. A nice
outcome of this choice is that minutes
does not go above 32 bits.
[2016-04-23, Tim Song comments]
The P/R of LWG 2592 doesn't fix the issue it wants to solve, because the actual underlying type will likely have more bits than the specified minimum.
Considerseconds
, which the P/R requires to have at least 37 bits. On a typical system this implies
using a 64-bit integer. To ensure that casting from seconds::max()
to minutes
doesn't overflow
in such a system, it is necessary for the latter to have at least 59 bits (which means, in practice, 64 bits too),
not just 32 bits. Thus, just changing the minimum number of bits will not be able to provide the desired guarantee
that casting from a smaller unit to a larger one never overflow.
If such a guarantee is to be provided, it needs to be spelled out directly. Note that the difference here is 9 bits
(for the 1000-fold case) and 5 bits (for the 60-fold case), which is less than the size difference between integer
types on common systems, so such a requirement would effectively require those convenience typedefs to use the
same underlying integer type.
Proposed resolution:
This wording is relative to N4567.
Change 30.2 [time.syn], header <chrono>
synopsis, as indicated
[…] // convenience typedefs typedef duration<signed integer type of at least 64 bits, nano> nanoseconds; typedef duration<signed integer type of at least 55 bits, micro> microseconds; typedef duration<signed integer type of at least 4645bits, milli> milliseconds; typedef duration<signed integer type of at least 3735bits > seconds; typedef duration<signed integer type of at least 3229bits, ratio< 60>> minutes; typedef duration<signed integer type of at least 2723bits, ratio<3600>> hours; […]