I'm currently rounding a std::chrono::duration<float> to std::chrono::duration<uint32_t>, and when I give it out-of-range values it rounds to 1s instead of 4294967295s.
Looking at the standard, it says
template <class ToDuration, class Rep, class Period> constexpr ToDuration round(const duration<Rep, Period>& d);[...]
Returns: The value t representable inToDurationthat is the closest tod. [...]
Here is my exact code:
#include <chrono>
#include <cstdio>
#include <limits>
#include <cstdint>
int main()
{
std::chrono::duration<float> seconds{std::numeric_limits<float>::max()};
printf("float: %f\n", seconds.count());
printf("uint32_t: %u\n", std::chrono::round<std::chrono::duration<uint32_t>>(seconds).count());
printf(" int32_t: %d\n", std::chrono::round<std::chrono::duration<int32_t>>(seconds).count());
printf("uint64_t: %lu\n", std::chrono::round<std::chrono::duration<uint64_t>>(seconds).count());
printf(" int64_t: %ld\n", std::chrono::round<std::chrono::duration<int64_t>>(seconds).count());
}
which outputs
float: 340282346638528859811704183484516925440.000000
uint32_t: 1
int32_t: -2147483647
uint64_t: 9223372036854775809
int64_t: -9223372036854775807
As you can see, other integer types also behave strangely. Unlike std::lround et al., std::chrono::round doesn't say anything about being undefined if the floating-point input is out of range.
Am I missing anything?
(For context, I compiled this with clang version 14.0.0-1ubuntu1.1 on x86_64, but I first noticed the issue on an ARMv7 system using gcc.)
duration<Rep, Ratio>is a simple, thin wrapper aroundRep. In your exampleRepisfloatin the argument toroundanduint32_tin the result.As a thin wrapper,
durationdoes not alter the fundamental behavior of theRepfor things like overflow and conversion. To do so would add overhead when it is often not desirable.If specific behavior such as overflow checking is desired,
chronofacilitates that by allowingduration<safe_int>wheresafe_intis a hypothetical class type that emulates integral arithmetic but checks for overflow. Real world examples of such libraries exist, and do not require special adaptation to be used withchrono.For just
floatanduint32_tas asked about in this question, the undefined behavior results at thefloatanduint32_tand level, not at thechronolevel. Specifically, when convertingfloattouint32_t, the behavior is undefined if the truncated value in thefloatcan not be represented in theuint32_t.There is an open question as to whether the standard actually says this or not as discussed in the comments below. To pursue this further, an interested party should submit an LWG issue:
http://cplusplus.github.io/LWG/lwg-active.html#submit_issue
And then the LWG will decide if there is a bug, and if so, how best to remedy it.
Pertinent links: