diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format index f4d915176561a6ca7f130f2e1a088f39b37c9e10..0eca8b58bfa8d6abadb7c66962daf70601b419b6 100644 --- a/libstdc++-v3/include/std/format +++ b/libstdc++-v3/include/std/format @@ -1623,6 +1623,7 @@ namespace __format *__p = std::toupper(*__p); } + bool __have_sign = true; // Add sign for non-negative values. if (!__builtin_signbit(__v)) { @@ -1630,56 +1631,73 @@ namespace __format *--__start = '+'; else if (_M_spec._M_sign == _Sign_space) *--__start = ' '; + else + __have_sign = false; } string_view __narrow_str(__start, __res.ptr - __start); - // Use alternate form. + // Use alternate form. Ensure decimal point is always present, + // and add trailing zeros (up to precision) for g and G forms. if (_M_spec._M_alt && __builtin_isfinite(__v)) { string_view __s = __narrow_str; - size_t __z = 0; - size_t __p; - size_t __d = __s.find('.'); - size_t __sigfigs; - if (__d != __s.npos) + size_t __sigfigs; // Number of significant figures. + size_t __z = 0; // Number of trailing zeros to add. + size_t __p; // Position of the exponent character (if any). + size_t __d = __s.find('.'); // Position of decimal point. + if (__d != __s.npos) // Found decimal point. { __p = __s.find(__expc, __d + 1); if (__p == __s.npos) __p = __s.size(); - __sigfigs = __p - 1; + + // If presentation type is g or G we might need to add zeros. + if (__trailing_zeros) + { + // Find number of digits after first significant figure. + if (__s[__have_sign] != '0') + // A string like "D.D" or "-D.DDD" + __sigfigs = __p - __have_sign - 1; + else + // A string like "0.D" or "-0.0DD". + // Safe to assume there is a non-zero digit, because + // otherwise there would be no decimal point. + __sigfigs = __p - __s.find_first_not_of('0', __d + 1); + } } - else + else // No decimal point, we need to insert one. { - __p = __s.find(__expc); + __p = __s.find(__expc); // Find the exponent, if present. if (__p == __s.npos) __p = __s.size(); __d = __p; // Position where '.' should be inserted. - __sigfigs = __d; + __sigfigs = __d - __have_sign; } if (__trailing_zeros && __prec != 0) { - if (!__format::__is_xdigit(__s[0])) - --__sigfigs; - __z = __prec - __sigfigs; // Number of zeros to insert. + // For g and G presentation types std::to_chars produces + // no more than prec significant figures. Insert this many + // zeros so the result has exactly prec significant figures. + __z = __prec - __sigfigs; } - if (size_t __extras = int(__d == __p) + __z) + if (size_t __extras = int(__d == __p) + __z) // How many to add. { if (__dynbuf.empty() && __extras <= size_t(__end - __res.ptr)) { + // The stack buffer is large enough for the result. // Move exponent to make space for extra chars. __builtin_memmove(__start + __p + __extras, __start + __p, __s.size() - __p); - if (__d == __p) __start[__p++] = '.'; __builtin_memset(__start + __p, '0', __z); __narrow_str = {__s.data(), __s.size() + __extras}; } - else + else // Need to switch to the dynamic buffer. { __dynbuf.reserve(__s.size() + __extras); if (__dynbuf.empty()) @@ -1689,6 +1707,7 @@ namespace __format __dynbuf += '.'; if (__z) __dynbuf.append(__z, '0'); + __dynbuf.append(__s.substr(__p)); } else { diff --git a/libstdc++-v3/testsuite/std/format/functions/format.cc b/libstdc++-v3/testsuite/std/format/functions/format.cc index 30c5fc222377331c655770165df59dfcb28e4428..a27fbe74631b79e8e3585c472cb54849c77d2d22 100644 --- a/libstdc++-v3/testsuite/std/format/functions/format.cc +++ b/libstdc++-v3/testsuite/std/format/functions/format.cc @@ -181,6 +181,12 @@ test_alternate_forms() // PR libstdc++/108046 s = std::format("{0:#.0} {0:#.1} {0:#.0g}", 10.0); VERIFY( s == "1.e+01 1.e+01 1.e+01" ); + + // PR libstdc++/113512 + s = std::format("{:#.3g}", 0.025); + VERIFY( s == "0.0250" ); + s = std::format("{:#07.3g}", 0.02); + VERIFY( s == "00.0200" ); } void