diff --git a/libcxx/include/__format/formatter_floating_point.h b/libcxx/include/__format/formatter_floating_point.h --- a/libcxx/include/__format/formatter_floating_point.h +++ b/libcxx/include/__format/formatter_floating_point.h @@ -14,6 +14,7 @@ #include <__algorithm/copy_n.h> #include <__algorithm/fill_n.h> #include <__algorithm/find.h> +#include <__algorithm/max.h> #include <__algorithm/min.h> #include <__algorithm/rotate.h> #include <__algorithm/transform.h> @@ -183,6 +184,7 @@ _LIBCPP_HIDE_FROM_ABI int __precision() const { return __precision_; } _LIBCPP_HIDE_FROM_ABI int __num_trailing_zeros() const { return __num_trailing_zeros_; } _LIBCPP_HIDE_FROM_ABI void __remove_trailing_zeros() { __num_trailing_zeros_ = 0; } + _LIBCPP_HIDE_FROM_ABI void __add_trailing_zeros(int __zeros) { __num_trailing_zeros_ += __zeros; } private: int __precision_; @@ -216,7 +218,7 @@ /// \returns a pointer to the exponent or __last when not found. constexpr inline _LIBCPP_HIDE_FROM_ABI char* __find_exponent(char* __first, char* __last) { ptrdiff_t __size = __last - __first; - if (__size > 4) { + if (__size >= 4) { __first = __last - _VSTD::min(__size, ptrdiff_t(6)); for (; __first != __last - 3; ++__first) { if (*__first == 'e') @@ -404,6 +406,7 @@ // In fixed mode the algorithm truncates trailing spaces and possibly the // radix point. There's no good guess for the position of the radix point // therefore scan the output after the first digit. + __result.__radix_point = _VSTD::find(__first, __result.__last, '.'); } } @@ -454,7 +457,10 @@ char* __first = __formatter::__insert_sign(__buffer.begin(), __negative, __sign); switch (__type) { case __format_spec::__type::__default: - return __formatter::__format_buffer_default(__buffer, __value, __first); + if (__has_precision) + return __formatter::__format_buffer_general_lower_case(__buffer, __value, __buffer.__precision(), __first); + else + return __formatter::__format_buffer_default(__buffer, __value, __first); case __format_spec::__type::__hexfloat_lower_case: return __formatter::__format_buffer_hexadecimal_lower_case( @@ -625,20 +631,51 @@ __float_result __result = __formatter::__format_buffer( __buffer, __value, __negative, (__specs.__has_precision()), __specs.__std_.__sign_, __specs.__std_.__type_); - if (__specs.__std_.__alternate_form_ && __result.__radix_point == __result.__last) { - *__result.__last++ = '.'; - - // When there is an exponent the point needs to be moved before the - // exponent. When there's no exponent the rotate does nothing. Since - // rotate tests whether the operation is a nop, call it unconditionally. - _VSTD::rotate(__result.__exponent, __result.__last - 1, __result.__last); - __result.__radix_point = __result.__exponent; + if (__specs.__std_.__alternate_form_) { + if (__result.__radix_point == __result.__last) { + *__result.__last++ = '.'; + + // When there is an exponent the point needs to be moved before the + // exponent. When there's no exponent the rotate does nothing. Since + // rotate tests whether the operation is a nop, call it unconditionally. + _VSTD::rotate(__result.__exponent, __result.__last - 1, __result.__last); + __result.__radix_point = __result.__exponent; + + // The radix point is always placed before the exponent. + // - No exponent needs to point to the new last. + // - An exponent needs to move one position to the right. + // So it's safe to increment the value unconditionally. + ++__result.__exponent; + } - // The radix point is always placed before the exponent. - // - No exponent needs to point to the new last. - // - An exponent needs to move one position to the right. - // So it's safe to increment the value unconditionally. - ++__result.__exponent; + // [format.string.std]/6 + // In addition, for g and G conversions, trailing zeros are not removed + // from the result. + // + // If the type option for a floating-point type is none it may use the + // general formatting, but it's not a g or G conversion. So in that case + // the formatting should not append trailing zeros. + bool __is_general = __specs.__std_.__type_ == __format_spec::__type::__general_lower_case || + __specs.__std_.__type_ == __format_spec::__type::__general_upper_case; + + if (__is_general) { + // https://en.cppreference.com/w/c/io/fprintf + // Let P equal the precision if nonzero, 6 if the precision is not + // specified, or 1 if the precision is 0. Then, if a conversion with + // style E would have an exponent of X: + int __p = _VSTD::max(1, (__specs.__has_precision() ? __specs.__precision_ : 6)); + if (__result.__exponent == __result.__last) + // if P > X >= -4, the conversion is with style f or F and precision P - 1 - X. + // By including the radix point it calculates P - (1 + X) + __p -= __result.__radix_point - __buffer.begin(); + else + // otherwise, the conversion is with style e or E and precision P - 1. + --__p; + + ptrdiff_t __precision = (__result.__exponent - __result.__radix_point) - 1; + if (__precision < __p) + __buffer.__add_trailing_zeros(__p - __precision); + } } # ifndef _LIBCPP_HAS_NO_LOCALIZATION diff --git a/libcxx/include/__format/parser_std_format_spec.h b/libcxx/include/__format/parser_std_format_spec.h --- a/libcxx/include/__format/parser_std_format_spec.h +++ b/libcxx/include/__format/parser_std_format_spec.h @@ -653,11 +653,6 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_floating_point(__parser<_CharT>& __parser) { switch (__parser.__type_) { case __format_spec::__type::__default: - // When no precision specified then it keeps default since that - // formatting differs from the other types. - if (__parser.__precision_as_arg_ || __parser.__precision_ != -1) - __parser.__type_ = __format_spec::__type::__general_lower_case; - break; case __format_spec::__type::__hexfloat_lower_case: case __format_spec::__type::__hexfloat_upper_case: // Precision specific behavior will be handled later. diff --git a/libcxx/test/std/utilities/format/format.functions/format_tests.h b/libcxx/test/std/utilities/format/format.functions/format_tests.h --- a/libcxx/test/std/utilities/format/format.functions/format_tests.h +++ b/libcxx/test/std/utilities/format/format.functions/format_tests.h @@ -2032,8 +2032,8 @@ check.template operator()<"answer is '{:.0g}'">(SV("answer is '0'"), F(0)); check.template operator()<"answer is '{:#.0g}'">(SV("answer is '0.'"), F(0)); - check.template operator()<"answer is '{:#g}'">(SV("answer is '0.'"), F(0)); - check.template operator()<"answer is '{:#g}'">(SV("answer is '2.5'"), F(2.5)); + check.template operator()<"answer is '{:#g}'">(SV("answer is '0.00000'"), F(0)); + check.template operator()<"answer is '{:#g}'">(SV("answer is '2.50000'"), F(2.5)); check.template operator()<"answer is '{:#g}'">(SV("answer is 'inf'"), std::numeric_limits::infinity()); check.template operator()<"answer is '{:#g}'">(SV("answer is '-inf'"), -std::numeric_limits::infinity()); @@ -2081,6 +2081,33 @@ check.template operator()<"answer is '{:.5g}'">(SV("answer is '0.03125'"), 0.03125); check.template operator()<"answer is '{:.10g}'">(SV("answer is '0.03125'"), 0.03125); + // *** precision & alternate form *** + + // Output validated with printf("%#xg") + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1.'"), 1.2, 0); + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1.'"), 1.2, 1); + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1.2'"), 1.2, 2); + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1.20'"), 1.2, 3); + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1.200'"), 1.2, 4); + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1.2000'"), 1.2, 5); + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1.20000'"), 1.2, 6); + + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1.e+03'"), 1200.0, 0); + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1.e+03'"), 1200.0, 1); + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1.2e+03'"), 1200.0, 2); + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1.20e+03'"), 1200.0, 3); + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1200.'"), 1200.0, 4); + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1200.0'"), 1200.0, 5); + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1200.00'"), 1200.0, 6); + + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1.e+06'"), 1200000.0, 0); + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1.e+06'"), 1200000.0, 1); + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1.2e+06'"), 1200000.0, 2); + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1.20e+06'"), 1200000.0, 3); + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1.200e+06'"), 1200000.0, 4); + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1.2000e+06'"), 1200000.0, 5); + check.template operator()<"answer is '{:#.{}g}'">(SV("answer is '1.20000e+06'"), 1200000.0, 6); + // *** locale-specific form *** // See locale-specific_form.pass.cpp } @@ -2158,8 +2185,8 @@ check.template operator()<"answer is '{:.0G}'">(SV("answer is '0'"), F(0)); check.template operator()<"answer is '{:#.0G}'">(SV("answer is '0.'"), F(0)); - check.template operator()<"answer is '{:#G}'">(SV("answer is '0.'"), F(0)); - check.template operator()<"answer is '{:#G}'">(SV("answer is '2.5'"), F(2.5)); + check.template operator()<"answer is '{:#G}'">(SV("answer is '0.00000'"), F(0)); + check.template operator()<"answer is '{:#G}'">(SV("answer is '2.50000'"), F(2.5)); check.template operator()<"answer is '{:#G}'">(SV("answer is 'INF'"), std::numeric_limits::infinity()); check.template operator()<"answer is '{:#G}'">(SV("answer is '-INF'"), -std::numeric_limits::infinity()); @@ -2207,6 +2234,33 @@ check.template operator()<"answer is '{:.5G}'">(SV("answer is '0.03125'"), 0.03125); check.template operator()<"answer is '{:.10G}'">(SV("answer is '0.03125'"), 0.03125); + // *** precision & alternate form *** + + // Output validated with printf("%#xg") + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1.'"), 1.2, 0); + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1.'"), 1.2, 1); + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1.2'"), 1.2, 2); + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1.20'"), 1.2, 3); + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1.200'"), 1.2, 4); + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1.2000'"), 1.2, 5); + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1.20000'"), 1.2, 6); + + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1.E+03'"), 1200.0, 0); + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1.E+03'"), 1200.0, 1); + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1.2E+03'"), 1200.0, 2); + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1.20E+03'"), 1200.0, 3); + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1200.'"), 1200.0, 4); + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1200.0'"), 1200.0, 5); + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1200.00'"), 1200.0, 6); + + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1.E+06'"), 1200000.0, 0); + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1.E+06'"), 1200000.0, 1); + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1.2E+06'"), 1200000.0, 2); + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1.20E+06'"), 1200000.0, 3); + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1.200E+06'"), 1200000.0, 4); + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1.2000E+06'"), 1200000.0, 5); + check.template operator()<"answer is '{:#.{}G}'">(SV("answer is '1.20000E+06'"), 1200000.0, 6); + // *** locale-specific form *** // See locale-specific_form.pass.cpp } @@ -2450,6 +2504,32 @@ check.template operator()<"answer is '{:.5}'">(SV("answer is '0.03125'"), 0.03125); check.template operator()<"answer is '{:.10}'">(SV("answer is '0.03125'"), 0.03125); + // *** precision & alternate form *** + + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1.'"), 1.2, 0); + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1.'"), 1.2, 1); + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1.2'"), 1.2, 2); + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1.2'"), 1.2, 3); + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1.2'"), 1.2, 4); + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1.2'"), 1.2, 5); + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1.2'"), 1.2, 6); + + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1.e+03'"), 1200.0, 0); + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1.e+03'"), 1200.0, 1); + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1.2e+03'"), 1200.0, 2); + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1.2e+03'"), 1200.0, 3); + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1200.'"), 1200.0, 4); + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1200.'"), 1200.0, 5); + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1200.'"), 1200.0, 6); + + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1.e+06'"), 1200000.0, 0); + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1.e+06'"), 1200000.0, 1); + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1.2e+06'"), 1200000.0, 2); + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1.2e+06'"), 1200000.0, 3); + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1.2e+06'"), 1200000.0, 4); + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1.2e+06'"), 1200000.0, 5); + check.template operator()<"answer is '{:#.{}}'">(SV("answer is '1.2e+06'"), 1200000.0, 6); + // *** locale-specific form *** // See locale-specific_form.pass.cpp }