Tested aarch64-linux. Pushed to trunk. Backport to gcc-13 to follow.

-- >8 --

The logic for handling '#' forms was ... not good. The count of
significant figures just counted digits, instead of ignoring leading
zeros. And when moving the result from the stack buffer to a dynamic
string the exponent could get lost in some cases.

libstdc++-v3/ChangeLog:

        PR libstdc++/113512
        * include/std/format (__formatter_fp::format): Fix logic for
        alternate forms.
        * testsuite/std/format/functions/format.cc: Check buggy cases of
        alternate forms with g presentation type.
---
 libstdc++-v3/include/std/format               | 51 +++++++++++++------
 .../testsuite/std/format/functions/format.cc  |  6 +++
 2 files changed, 41 insertions(+), 16 deletions(-)

diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index f4d91517656..0eca8b58bfa 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 30c5fc22237..a27fbe74631 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
-- 
2.43.0

Reply via email to