Check the truncated count in float to integral duration_cast

This commit is contained in:
Aizal Khan 2026-10-25 11:56:49 +05:30
parent 7dc456208e
commit c9b66420b5
2 changed files with 30 additions and 1 deletions

View File

@ -524,11 +524,13 @@ auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
auto count = detail::duration_cast<
std::chrono::duration<FromRep, typename To::period>>(from)
.count();
// The conversion truncates toward zero so check the truncated value.
// max + 1 is a power of 2 so the cast to FromRep is exact even when max
// itself is rounded up by the conversion; the comparison is false for NaN.
auto upper = static_cast<FromRep>(max_value<to_rep>()) + 1;
auto lower = static_cast<FromRep>((std::numeric_limits<to_rep>::min)());
if (!(count >= lower && count < upper)) throw_duration_error();
auto truncated = std::trunc(count);
if (!(truncated >= lower && truncated < upper)) throw_duration_error();
return To(static_cast<to_rep>(count));
#else
return std::chrono::duration_cast<To>(from);

View File

@ -1006,6 +1006,33 @@ TEST(chrono_test, out_of_range) {
EXPECT_THROW((void)fmt::format("{:%F}", flocal), fmt::format_error);
}
TEST(chrono_test, float_to_integral_duration_cast) {
using fsec = std::chrono::duration<double>;
// Values that truncate toward zero into range are valid.
EXPECT_EQ(
fmt::detail::duration_cast<std::chrono::duration<unsigned>>(fsec(-0.5))
.count(),
0u);
auto min = (std::numeric_limits<int>::min)();
EXPECT_EQ(
fmt::detail::duration_cast<std::chrono::duration<int>>(fsec(min - 0.5))
.count(),
min);
auto max = (std::numeric_limits<int>::max)();
EXPECT_EQ(
fmt::detail::duration_cast<std::chrono::duration<int>>(fsec(max + 0.5))
.count(),
max);
if (FMT_SAFE_DURATION_CAST) {
EXPECT_THROW(
fmt::detail::duration_cast<std::chrono::duration<unsigned>>(fsec(-1)),
fmt::format_error);
EXPECT_THROW(
fmt::detail::duration_cast<std::chrono::duration<int>>(fsec(min - 1.0)),
fmt::format_error);
}
}
TEST(chrono_test, calendar_padding) {
EXPECT_EQ("05 ", fmt::format("{:5}", fmt::day(5)));
EXPECT_EQ(" Jan", fmt::format("{:>5}", fmt::month(1)));