2592. Require that chrono::duration_casts from smaller durations to larger durations do not overflow

Section: 23.17.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 23.17.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.

The standard should specify that implementations guarantee that a 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:

Therefore, a minimum of 27 bits would be needed to store minutes if 32 were used to store seconds.

I propose to change the definitions of the convenience typedefs as follows:

// 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.

Consider seconds, 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.

  1. Change 23.17.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 4645 bits,       milli> milliseconds;
    typedef duration<signed integer type of at least 3735 bits             > seconds;
    typedef duration<signed integer type of at least 3229 bits, ratio<  60>> minutes;
    typedef duration<signed integer type of at least 2723 bits, ratio<3600>> hours;
    
    […]