-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
Brief description
The following code fails to compile due to an int addition overflow:
#include <chrono>
using namespace std::chrono;
constexpr auto wd = weekday(sys_days::max());
https://godbolt.org/z/K6dW7bvYq
Details
The overflow occurs in the evaluation of _Tp + 4 below
_NODISCARD static constexpr unsigned int _Weekday_from_days(int _Tp) noexcept {
return static_cast<unsigned int>(_Tp >= -4 ? (_Tp + 4) % 7 : (_Tp + 5) % 7 + 6);
}
Indeed, this addition overflows whenever _Tp > INT_MAX - 4.
Fixes
Provided that the range of long long int is larger than that of int, the obvious fix is declaring _Tp as long long int and an alternative is this:
_NODISCARD static constexpr unsigned int _Weekday_from_days(int _Tp) noexcept {
auto const m = unsigned(_Tp) + (_Tp >= 0 ? 4 : 0);
auto const w = m % 7;
__assume(w != 7); // not necessary but improves codegen
return w;
}
This alternative is better than the obvious fix and better than the current implementation. Indeed, with this fix the codegen of weekday::weekday(const sys_days&) becomes branch-free and avoids div instructions on x64, x86 and arm64.
Test
The alternative above works as an exhaustive test shows:
int main() {
unsigned int encoding = weekday(sys_days::min()).c_encoding();
for (auto s = sys_days::min(); s < sys_days::max(); s += days(1)) {
assert(weekday_ctr(s).c_encoding() == encoding);
++encoding;
if (encoding == 7)
encoding = 0;
}
// One more to go:
assert(weekday_ctr(sys_days::max()).c_encoding() == encoding);
std::puts("Success.");
}
(where weekday_ctr is an impersonation of weekday::weekday(const sys_days&) using the fixed _Weekday_from_days.)