libstdc++: Fix formatting of most negative chrono::duration [PR116755]

When formatting chrono::duration<signed-integer-type, P>::min() we were
causing undefined behaviour by trying to form the negative of the most
negative value. If we convert negative durations with integer rep to the
corresponding unsigned integer rep then we can safely represent all
values.

libstdc++-v3/ChangeLog:

	PR libstdc++/116755
	* include/bits/chrono_io.h (formatter<duration<R,P>>::format):
	Cast negative integral durations to unsigned rep.
	* testsuite/20_util/duration/io.cc: Test the most negative
	integer durations.
This commit is contained in:
Jonathan Wakely 2024-09-18 17:20:29 +01:00 committed by Jonathan Wakely
parent b6463161c3
commit 482e651f57
No known key found for this signature in database
2 changed files with 22 additions and 2 deletions

View file

@ -1720,8 +1720,20 @@ namespace __format
basic_format_context<_Out, _CharT>& __fc) const
{
if constexpr (numeric_limits<_Rep>::is_signed)
if (__d < __d.zero())
return _M_f._M_format(-__d, __fc, true);
if (__d < __d.zero()) [[unlikely]]
{
if constexpr (is_integral_v<_Rep>)
{
// -d is undefined for the most negative integer.
// Convert duration to corresponding unsigned rep.
using _URep = make_unsigned_t<_Rep>;
auto __ucnt = -static_cast<_URep>(__d.count());
auto __ud = chrono::duration<_URep, _Period>(__ucnt);
return _M_f._M_format(__ud, __fc, true);
}
else
return _M_f._M_format(-__d, __fc, true);
}
return _M_f._M_format(__d, __fc, false);
}

View file

@ -106,6 +106,14 @@ test_format()
VERIFY( s == "500ms" );
s = std::format("{:%Q %q}", u);
VERIFY( s == "500 ms" );
// PR libstdc++/116755 extra minus sign for most negative value
auto minsec = std::chrono::seconds::min();
s = std::format("{}", minsec);
auto expected = std::format("{}s", minsec.count());
VERIFY( s == expected );
s = std::format("{:%Q%q}", minsec);
VERIFY( s == expected );
}
void