libstdc++: Fix parsing of fractional seconds [PR114244]
When converting a chrono::duration<long double> to a result type with an integer representation we should use chrono::round<_Duration> so that we don't truncate towards zero. Rounding ensures that e.g. 0.001999s becomes 2ms not 1ms. We can also remove some redundant uses of chrono::duration_cast to convert from seconds to _Duration, because the _Parser class template requires _Duration type to be able to represent seconds without loss of precision. This also fixes a bug where no fractional part would be parsed for chrono::duration<long double> because its period is ratio<1>. We should also consider treat_as_floating_point<rep> when deciding whether to skip reading a fractional part. libstdc++-v3/ChangeLog: PR libstdc++/114244 * include/bits/chrono_io.h (_Parser::operator()): Remove redundant uses of duration_cast. Use chrono::round to convert long double value to durations with integer representations. Check represenation type when deciding whether to skip parsing fractional seconds. * testsuite/20_util/duration/114244.cc: New test. * testsuite/20_util/duration/io.cc: Check that a floating-point duration with ratio<1> precision can be parsed.
This commit is contained in:
parent
9ccd03dee4
commit
5f9d7a5b6c
3 changed files with 60 additions and 6 deletions
|
@ -3113,6 +3113,9 @@ namespace __detail
|
|||
unsigned __num = 0; // Non-zero for N modifier.
|
||||
bool __is_flag = false; // True if we're processing a % flag.
|
||||
|
||||
constexpr bool __is_floating
|
||||
= treat_as_floating_point_v<typename _Duration::rep>;
|
||||
|
||||
// If an out-of-range value is extracted (e.g. 61min for %M),
|
||||
// do not set failbit immediately because we might not need it
|
||||
// (e.g. parsing chrono::year doesn't care about invalid %M values).
|
||||
|
@ -3195,7 +3198,7 @@ namespace __detail
|
|||
__d = day(__tm.tm_mday);
|
||||
__h = hours(__tm.tm_hour);
|
||||
__min = minutes(__tm.tm_min);
|
||||
__s = duration_cast<_Duration>(seconds(__tm.tm_sec));
|
||||
__s = seconds(__tm.tm_sec);
|
||||
}
|
||||
}
|
||||
__parts |= _ChronoParts::_DateTime;
|
||||
|
@ -3564,8 +3567,8 @@ namespace __detail
|
|||
if (!__is_failed(__err))
|
||||
__s = seconds(__tm.tm_sec);
|
||||
}
|
||||
else if constexpr (ratio_equal_v<typename _Duration::period,
|
||||
ratio<1>>)
|
||||
else if constexpr (_Duration::period::den == 1
|
||||
&& !__is_floating)
|
||||
{
|
||||
auto __val = __read_unsigned(__num ? __num : 2);
|
||||
if (0 <= __val && __val <= 59) [[likely]]
|
||||
|
@ -3577,7 +3580,7 @@ namespace __detail
|
|||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
else // Read fractional seconds
|
||||
{
|
||||
basic_stringstream<_CharT> __buf;
|
||||
auto __digit = _S_try_read_digit(__is, __err);
|
||||
|
@ -3626,7 +3629,10 @@ namespace __detail
|
|||
else
|
||||
{
|
||||
duration<long double> __fs(__val);
|
||||
__s = duration_cast<_Duration>(__fs);
|
||||
if constexpr (__is_floating)
|
||||
__s = __fs;
|
||||
else
|
||||
__s = chrono::round<_Duration>(__fs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3737,7 +3743,7 @@ namespace __detail
|
|||
{
|
||||
__h = hours(__tm.tm_hour);
|
||||
__min = minutes(__tm.tm_min);
|
||||
__s = duration_cast<_Duration>(seconds(__tm.tm_sec));
|
||||
__s = seconds(__tm.tm_sec);
|
||||
}
|
||||
}
|
||||
__parts |= _ChronoParts::_TimeOfDay;
|
||||
|
|
36
libstdc++-v3/testsuite/20_util/duration/114244.cc
Normal file
36
libstdc++-v3/testsuite/20_util/duration/114244.cc
Normal file
|
@ -0,0 +1,36 @@
|
|||
// { dg-do run { target c++20 } }
|
||||
// { dg-timeout-factor 2 }
|
||||
// { dg-require-namedlocale "en_US.ISO8859-1" }
|
||||
|
||||
// PR libstdc++/114244 Need to use round when parsing fractional seconds
|
||||
|
||||
#include <chrono>
|
||||
#include <sstream>
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
void
|
||||
test_pr114244()
|
||||
{
|
||||
using namespace std::chrono;
|
||||
seconds s;
|
||||
milliseconds ms;
|
||||
microseconds us;
|
||||
|
||||
std::istringstream is;
|
||||
|
||||
is.clear();
|
||||
is.str("0.002");
|
||||
VERIFY( is >> parse("%S", ms) );
|
||||
VERIFY( ms == 2ms ); // not 1ms
|
||||
|
||||
is.imbue(std::locale(ISO_8859(1,en_US)));
|
||||
is.clear();
|
||||
is.str("0.002");
|
||||
VERIFY( is >> parse("%S", us) );
|
||||
VERIFY( us == 2000us ); // not 1999us
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_pr114244();
|
||||
}
|
|
@ -200,6 +200,18 @@ test_parse()
|
|||
VERIFY( is >> parse("%S", us) );
|
||||
VERIFY( us == 976us );
|
||||
VERIFY( is.get() == '5' );
|
||||
|
||||
is.clear();
|
||||
is.str("0.5");
|
||||
std::chrono::duration<double> ds;
|
||||
VERIFY( is >> parse("%S", ds) );
|
||||
VERIFY( ds == 0.5s );
|
||||
|
||||
is.clear();
|
||||
is.str("0.125");
|
||||
std::chrono::duration<double, std::milli> dms;
|
||||
VERIFY( is >> parse("%S", dms) );
|
||||
VERIFY( dms == 0.125s );
|
||||
}
|
||||
|
||||
int main()
|
||||
|
|
Loading…
Add table
Reference in a new issue