PR #2195: StrFormat: honor zero padding in slow scientific conversions

Imported from GitHub PR https://github.com/abseil/abseil-cpp/pull/2195

Slow scientific `%e` and `%g` conversions dropped zero padding: `%010.0e` and `%010.1g` returned `3e+38` instead of `000003e+38` for `0x1p128`.

Both emitters now append the computed zeros after any sign.

The regression covers both exponent signs, positive and negative values, lowercase and uppercase conversions, sign flags, and alternate form. It fails before and passes after the fix. All eight existing str_format test targets pass in both runs with GCC 13.3, C++17, and CMake Release.

Fixes #2194

Merge 7429268255037cd3ae1e02d354b66793f047e183 into d522178b54087d2c3855484c1899ce99ad278169

Merging this change closes #2195

PiperOrigin-RevId: 999151724
diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc
index 16fcaa9..cd60ef3 100644
--- a/absl/strings/internal/str_format/convert_test.cc
+++ b/absl/strings/internal/str_format/convert_test.cc
@@ -47,6 +47,7 @@
 #include "absl/strings/internal/str_format/arg.h"
 #include "absl/strings/internal/str_format/bind.h"
 #include "absl/strings/match.h"
+#include "absl/strings/str_cat.h"
 #include "absl/strings/str_format.h"
 #include "absl/strings/string_view.h"
 #include "absl/types/span.h"
@@ -1017,6 +1018,35 @@
   }
 }
 
+TEST_F(FormatConvertTest, ScientificZeroPadding) {
+  struct Case {
+    absl::string_view format;
+    // Positive and negative expected prefixes.
+    absl::string_view positive;
+    absl::string_view negative;
+  } cases[] = {
+      {"%010.0e", "000003e", "-00003e"},  {"%010.1g", "000003e", "-00003e"},
+      {"%010.0E", "000003E", "-00003E"},  {"%010.1G", "000003E", "-00003E"},
+      {"%+010.0e", "+00003e", "-00003e"}, {"%+010.1g", "+00003e", "-00003e"},
+      {"% 010.0e", " 00003e", "-00003e"}, {"% 010.1g", " 00003e", "-00003e"},
+      {"%#010.0e", "00003.e", "-0003.e"}, {"%#010.1g", "00003.e", "-0003.e"},
+  };
+
+  // These exact values exercise both slow scientific conversion paths.
+  for (double value : {0x1p128, 0x1p-128}) {
+    const absl::string_view exponent = value > 1 ? "+38" : "-39";
+    for (const auto& c : cases) {
+      SCOPED_TRACE(absl::StrCat(value, ", ", c.format));
+      EXPECT_EQ(
+          FormatPack(UntypedFormatSpecImpl(c.format), {FormatArgImpl(value)}),
+          absl::StrCat(c.positive, exponent));
+      EXPECT_EQ(
+          FormatPack(UntypedFormatSpecImpl(c.format), {FormatArgImpl(-value)}),
+          absl::StrCat(c.negative, exponent));
+    }
+  }
+}
+
 TEST_F(FormatConvertTest, DoubleRound) {
   std::string s;
   const auto format = [&](const char *fmt, double d) -> std::string & {
diff --git a/absl/strings/internal/str_format/float_conversion.cc b/absl/strings/internal/str_format/float_conversion.cc
index 137e159..0895c31 100644
--- a/absl/strings/internal/str_format/float_conversion.cc
+++ b/absl/strings/internal/str_format/float_conversion.cc
@@ -1496,6 +1496,7 @@
         if (state.sign_char != '\0') {
           state.sink->Append(1, state.sign_char);
         }
+        state.sink->Append(padding.zeros, '0');
 
         state.sink->Append(1, static_cast<char>(first_digit + '0'));
         if (precision > 0 || state.conv.has_alt_flag()) {
@@ -1599,6 +1600,7 @@
         if (state.sign_char != '\0') {
           state.sink->Append(1, state.sign_char);
         }
+        state.sink->Append(padding.zeros, '0');
         state.sink->Append(1, static_cast<char>(first_digit + '0'));
         --digits_to_go;
         if (print_dot) {