The standard does not currently specify how the precision value is interpreted if specify, only prohibit it from being used for formatting any other object than durations with floating point types.
This patch interprets user-specified duration value as follows: * if spec is empty for floating-point duration, the ostringstream is configured with precision value * for "%Q" is used for floating-point duration, the duration units are formated with format string that includes precision * for "%S" the precision controls the number of decimal digits for subseconds that is printed. With support in "%S" setting precision makes sense also for time-points. This patch takes simple approach of always allowing precision to be specified, and the value is ignored if no specifier are affected. We could also limit if to situations when _Subseconds or _EpochUnits for floating point rep are requested. Finally, for integral durations, the precision is trimmed to 18 digits, this can be adjusted, if we decide to go with direction of this patch. libstdc++-v3/ChangeLog: * include/bits/chrono_io.h (__foramtter_chrono::_M_parse): Parse precision value into _M_spec. (__formatter_chrono::_M_Q): Include precision in format string, if specified. (__formatter_duration::_M_format_to_ostream): Configure precision if provided. * testsuite/std/time/format/format.cc: Precision is now allowed. * testsuite/std/time/format/precision.cc: Adjusted tests. --- This patch is not meant to be landed now, it provides implementation exprience for one of the direction of resolving meaning of precision. libstdc++-v3/include/bits/chrono_io.h | 25 ++-- .../testsuite/std/time/format/format.cc | 2 +- .../testsuite/std/time/format/precision.cc | 113 +++++++++++++----- 3 files changed, 92 insertions(+), 48 deletions(-) diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h index d6bc6c7cf2a..a3264932024 100644 --- a/libstdc++-v3/include/bits/chrono_io.h +++ b/libstdc++-v3/include/bits/chrono_io.h @@ -594,19 +594,9 @@ namespace __format if (__finished()) return __first; - if (*__first == '.') - { - if ((__parts & _ChronoParts::_EpochUnits) == 0 - || !__spec._M_floating_point_rep) - __throw_format_error("format error: invalid precision for duration"); - - // Precision is allowed, but value is ignored. - __first = _Spec<_CharT>()._M_parse_precision(__first, __last, __pc); - // Still inditate that there was user supplied precision. - __spec._M_prec_kind = _WP_value; - if (__finished()) - return __first; - } + __first = __spec._M_parse_precision(__first, __last, __pc); + if (__finished()) + return __first; __spec._M_localized = false; __first = __spec._M_parse_locale(__first, __last); @@ -1499,7 +1489,12 @@ namespace __format _FormatContext& __ctx) const { // %Q The duration's numeric value. - return std::vformat_to(std::move(__out), _S_empty_spec, __t._M_ereps); + + __string_view __fs = _S_empty_spec; + if (_M_spec._M_floating_point_rep + && _M_spec._M_prec_kind != _WP_none) + __fs = _GLIBCXX_WIDEN("{0:.{2}}"); + return std::vformat_to(std::move(__out), __fs, __t._M_ereps); } template<typename _FormatContext> @@ -1903,6 +1898,8 @@ namespace __format if (__is_neg) [[unlikely]] __os << this->_S_plus_minus[1]; + if (_M_spec._M_prec_kind != _WP_none) + __os.precision(_M_spec._M_get_precision(__fc)); __os << __d; auto __str = std::move(__os).str(); diff --git a/libstdc++-v3/testsuite/std/time/format/format.cc b/libstdc++-v3/testsuite/std/time/format/format.cc index d6e35832cb5..474b1b5a938 100644 --- a/libstdc++-v3/testsuite/std/time/format/format.cc +++ b/libstdc++-v3/testsuite/std/time/format/format.cc @@ -56,7 +56,7 @@ test_bad_format_strings() VERIFY( not is_format_string_for("{:04%T}", t) ); // precision only valid for chrono::duration types with floating-point rep. - VERIFY( not is_format_string_for("{:.4}", t) ); + // VERIFY( not is_format_string_for("{:.4}", t) ); // unfinished format string VERIFY( not is_format_string_for("{:", t) ); diff --git a/libstdc++-v3/testsuite/std/time/format/precision.cc b/libstdc++-v3/testsuite/std/time/format/precision.cc index aa266156c1f..46a774f70f4 100644 --- a/libstdc++-v3/testsuite/std/time/format/precision.cc +++ b/libstdc++-v3/testsuite/std/time/format/precision.cc @@ -19,26 +19,25 @@ test_empty() res = std::format(WIDEN("{:}"), d); VERIFY( res == WIDEN("33.1112s") ); res = std::format(WIDEN("{:.0}"), d); - VERIFY( res == WIDEN("33.1112s") ); + VERIFY( res == WIDEN("3e+01s") ); res = std::format(WIDEN("{:.3}"), d); - VERIFY( res == WIDEN("33.1112s") ); + VERIFY( res == WIDEN("33.1s") ); res = std::format(WIDEN("{:.6}"), d); VERIFY( res == WIDEN("33.1112s") ); res = std::format(WIDEN("{:.9}"), d); - VERIFY( res == WIDEN("33.1112s") ); + VERIFY( res == WIDEN("33.111222s") ); - // Uses ostream operator<< const duration<double, std::nano> nd = d; res = std::format(WIDEN("{:}"), nd); VERIFY( res == WIDEN("3.31112e+10ns") ); res = std::format(WIDEN("{:.0}"), nd); - VERIFY( res == WIDEN("3.31112e+10ns") ); + VERIFY( res == WIDEN("3e+10ns") ); res = std::format(WIDEN("{:.3}"), nd); - VERIFY( res == WIDEN("3.31112e+10ns") ); + VERIFY( res == WIDEN("3.31e+10ns") ); res = std::format(WIDEN("{:.6}"), nd); VERIFY( res == WIDEN("3.31112e+10ns") ); res = std::format(WIDEN("{:.9}"), nd); - VERIFY( res == WIDEN("3.31112e+10ns") ); + VERIFY( res == WIDEN("3.3111222e+10ns") ); } template<typename CharT> @@ -51,11 +50,11 @@ test_Q() res = std::format(WIDEN("{:%Q}"), d); VERIFY( res == WIDEN("7.111222") ); res = std::format(WIDEN("{:.0%Q}"), d); - VERIFY( res == WIDEN("7.111222") ); + VERIFY( res == WIDEN("7") ); res = std::format(WIDEN("{:.3%Q}"), d); - VERIFY( res == WIDEN("7.111222") ); + VERIFY( res == WIDEN("7.11") ); res = std::format(WIDEN("{:.6%Q}"), d); - VERIFY( res == WIDEN("7.111222") ); + VERIFY( res == WIDEN("7.11122") ); res = std::format(WIDEN("{:.9%Q}"), d); VERIFY( res == WIDEN("7.111222") ); @@ -63,11 +62,11 @@ test_Q() res = std::format(WIDEN("{:%Q}"), md); VERIFY( res == WIDEN("7111.222") ); res = std::format(WIDEN("{:.0%Q}"), md); - VERIFY( res == WIDEN("7111.222") ); + VERIFY( res == WIDEN("7e+03") ); res = std::format(WIDEN("{:.3%Q}"), md); - VERIFY( res == WIDEN("7111.222") ); + VERIFY( res == WIDEN("7.11e+03") ); res = std::format(WIDEN("{:.6%Q}"), md); - VERIFY( res == WIDEN("7111.222") ); + VERIFY( res == WIDEN("7111.22") ); res = std::format(WIDEN("{:.9%Q}"), md); VERIFY( res == WIDEN("7111.222") ); @@ -75,13 +74,13 @@ test_Q() res = std::format(WIDEN("{:%Q}"), nd); VERIFY( res == WIDEN("7111222000") ); res = std::format(WIDEN("{:.0%Q}"), nd); - VERIFY( res == WIDEN("7111222000") ); + VERIFY( res == WIDEN("7e+09") ); res = std::format(WIDEN("{:.3%Q}"), nd); - VERIFY( res == WIDEN("7111222000") ); + VERIFY( res == WIDEN("7.11e+09") ); res = std::format(WIDEN("{:.6%Q}"), nd); - VERIFY( res == WIDEN("7111222000") ); + VERIFY( res == WIDEN("7.11122e+09") ); res = std::format(WIDEN("{:.9%Q}"), nd); - VERIFY( res == WIDEN("7111222000") ); + VERIFY( res == WIDEN("7.111222e+09") ); } template<typename CharT> @@ -97,45 +96,45 @@ test_S_fp() res = std::format(WIDEN("{:.0%S}"), d); VERIFY( res == WIDEN("05") ); res = std::format(WIDEN("{:.3%S}"), d); - VERIFY( res == WIDEN("05") ); + VERIFY( res == WIDEN("05.111") ); res = std::format(WIDEN("{:.6%S}"), d); - VERIFY( res == WIDEN("05") ); + VERIFY( res == WIDEN("05.111222") ); res = std::format(WIDEN("{:.9%S}"), d); - VERIFY( res == WIDEN("05") ); + VERIFY( res == WIDEN("05.111222000") ); duration<double, std::milli> md = d; res = std::format(WIDEN("{:%S}"), md); VERIFY( res == WIDEN("05.111") ); res = std::format(WIDEN("{:.0%S}"), md); - VERIFY( res == WIDEN("05.111") ); + VERIFY( res == WIDEN("05") ); res = std::format(WIDEN("{:.3%S}"), md); VERIFY( res == WIDEN("05.111") ); res = std::format(WIDEN("{:.6%S}"), md); - VERIFY( res == WIDEN("05.111") ); + VERIFY( res == WIDEN("05.111222") ); res = std::format(WIDEN("{:.9%S}"), md); - VERIFY( res == WIDEN("05.111") ); + VERIFY( res == WIDEN("05.111222000") ); duration<double, std::micro> ud = d; res = std::format(WIDEN("{:%S}"), ud); VERIFY( res == WIDEN("05.111222") ); res = std::format(WIDEN("{:.0%S}"), ud); - VERIFY( res == WIDEN("05.111222") ); + VERIFY( res == WIDEN("05") ); res = std::format(WIDEN("{:.3%S}"), ud); - VERIFY( res == WIDEN("05.111222") ); + VERIFY( res == WIDEN("05.111") ); res = std::format(WIDEN("{:.6%S}"), ud); VERIFY( res == WIDEN("05.111222") ); res = std::format(WIDEN("{:.9%S}"), ud); - VERIFY( res == WIDEN("05.111222") ); + VERIFY( res == WIDEN("05.111222000") ); duration<double, std::nano> nd = d; res = std::format(WIDEN("{:%S}"), nd); VERIFY( res == WIDEN("05.111222000") ); res = std::format(WIDEN("{:.0%S}"), nd); - VERIFY( res == WIDEN("05.111222000") ); + VERIFY( res == WIDEN("05") ); res = std::format(WIDEN("{:.3%S}"), nd); - VERIFY( res == WIDEN("05.111222000") ); + VERIFY( res == WIDEN("05.111") ); res = std::format(WIDEN("{:.6%S}"), nd); - VERIFY( res == WIDEN("05.111222000") ); + VERIFY( res == WIDEN("05.111222") ); res = std::format(WIDEN("{:.9%S}"), nd); VERIFY( res == WIDEN("05.111222000") ); @@ -143,13 +142,13 @@ test_S_fp() res = std::format(WIDEN("{:%S}"), pd); VERIFY( res == WIDEN("05.111222000000") ); res = std::format(WIDEN("{:.0%S}"), pd); - VERIFY( res == WIDEN("05.111222000000") ); + VERIFY( res == WIDEN("05") ); res = std::format(WIDEN("{:.3%S}"), pd); - VERIFY( res == WIDEN("05.111222000000") ); + VERIFY( res == WIDEN("05.111") ); res = std::format(WIDEN("{:.6%S}"), pd); - VERIFY( res == WIDEN("05.111222000000") ); + VERIFY( res == WIDEN("05.111222") ); res = std::format(WIDEN("{:.9%S}"), pd); - VERIFY( res == WIDEN("05.111222000000") ); + VERIFY( res == WIDEN("05.111222000") ); } template<typename CharT> @@ -162,23 +161,71 @@ test_S_int() auto d = floor<seconds>(src); res = std::format(WIDEN("{:%S}"), d); VERIFY( res == WIDEN("07") ); + res = std::format(WIDEN("{:.0%S}"), d); + VERIFY( res == WIDEN("07") ); + res = std::format(WIDEN("{:.3%S}"), d); + VERIFY( res == WIDEN("07.000") ); + res = std::format(WIDEN("{:.6%S}"), d); + VERIFY( res == WIDEN("07.000000") ); + res = std::format(WIDEN("{:.9%S}"), d); + VERIFY( res == WIDEN("07.000000000") ); + res = std::format(WIDEN("{:.12%S}"), d); + VERIFY( res == WIDEN("07.000000000000") ); auto md = floor<milliseconds>(src); res = std::format(WIDEN("{:%S}"), md); VERIFY( res == WIDEN("07.000") ); + res = std::format(WIDEN("{:.3%S}"), md); + VERIFY( res == WIDEN("07.000") ); + res = std::format(WIDEN("{:.6%S}"), md); + VERIFY( res == WIDEN("07.000000") ); + res = std::format(WIDEN("{:.9%S}"), md); + VERIFY( res == WIDEN("07.000000000") ); + res = std::format(WIDEN("{:.12%S}"), md); + VERIFY( res == WIDEN("07.000000000000") ); auto ud = floor<microseconds>(src); res = std::format(WIDEN("{:%S}"), ud); VERIFY( res == WIDEN("07.000012") ); + res = std::format(WIDEN("{:.0%S}"), ud); + VERIFY( res == WIDEN("07") ); + res = std::format(WIDEN("{:.3%S}"), ud); + VERIFY( res == WIDEN("07.000") ); + res = std::format(WIDEN("{:.6%S}"), ud); + VERIFY( res == WIDEN("07.000012") ); + res = std::format(WIDEN("{:.9%S}"), ud); + VERIFY( res == WIDEN("07.000012000") ); + res = std::format(WIDEN("{:.12%S}"), ud); + VERIFY( res == WIDEN("07.000012000000") ); auto nd = floor<nanoseconds>(src); res = std::format(WIDEN("{:%S}"), nd); VERIFY( res == WIDEN("07.000012345") ); + res = std::format(WIDEN("{:.0%S}"), nd); + VERIFY( res == WIDEN("07") ); + res = std::format(WIDEN("{:.3%S}"), nd); + VERIFY( res == WIDEN("07.000") ); + res = std::format(WIDEN("{:.6%S}"), nd); + VERIFY( res == WIDEN("07.000012") ); + res = std::format(WIDEN("{:.9%S}"), nd); + VERIFY( res == WIDEN("07.000012345") ); + res = std::format(WIDEN("{:.12%S}"), nd); + VERIFY( res == WIDEN("07.000012345000") ); using picoseconds = duration<unsigned long long, std::pico>; auto pd = floor<picoseconds>(src); res = std::format(WIDEN("{:%S}"), pd); VERIFY( res == WIDEN("07.000012345000") ); + res = std::format(WIDEN("{:.0%S}"), pd); + VERIFY( res == WIDEN("07") ); + res = std::format(WIDEN("{:.3%S}"), pd); + VERIFY( res == WIDEN("07.000") ); + res = std::format(WIDEN("{:.6%S}"), pd); + VERIFY( res == WIDEN("07.000012") ); + res = std::format(WIDEN("{:.9%S}"), pd); + VERIFY( res == WIDEN("07.000012345") ); + res = std::format(WIDEN("{:.12%S}"), pd); + VERIFY( res == WIDEN("07.000012345000") ); } template<typename CharT> -- 2.49.0