Add min exponent width option in double-to-string conversion (#116)

diff --git a/AUTHORS b/AUTHORS
index 3fcb1f7..4edeeed 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -13,3 +13,4 @@
 Kent Williams <chaircrusher@gmail.com>
 Elan Ruusamäe <glen@delfi.ee>
 Colin Hirsch <github@colin-hirsch.net>
+Zhenyi Peng <zhenyipeng@tencent.com>
diff --git a/Changelog b/Changelog
index a09940d..12b8a51 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,6 @@
+2019-08-01:
+  Add min exponent width option in double-to-string conversion.
+
 2019-06-22:
   Remove redundant parenthesis.
 
diff --git a/double-conversion/double-to-string.cc b/double-conversion/double-to-string.cc
index 13c7110..4562f99 100644
--- a/double-conversion/double-to-string.cc
+++ b/double-conversion/double-to-string.cc
@@ -97,6 +97,7 @@
     return;
   }
   DOUBLE_CONVERSION_ASSERT(exponent < 1e4);
+  // Changing this constant requires updating the comment of DoubleToStringConverter constructor
   const int kMaxExponentLength = 5;
   char buffer[kMaxExponentLength + 1];
   buffer[kMaxExponentLength] = '\0';
@@ -105,6 +106,11 @@
     buffer[--first_char_pos] = '0' + (exponent % 10);
     exponent /= 10;
   }
+  // Add prefix '0' to make exponent width >= min(min_exponent_with_, kMaxExponentLength)
+  // For example: convert 1e+9 -> 1e+09, if min_exponent_with_ is set to 2
+  while(kMaxExponentLength - first_char_pos < std::min(min_exponent_width_, kMaxExponentLength)) {
+    buffer[--first_char_pos] = '0';
+  }
   result_builder->AddSubstring(&buffer[first_char_pos],
                                kMaxExponentLength - first_char_pos);
 }
diff --git a/double-conversion/double-to-string.h b/double-conversion/double-to-string.h
index c1be34d..a44fa3c 100644
--- a/double-conversion/double-to-string.h
+++ b/double-conversion/double-to-string.h
@@ -104,6 +104,12 @@
   //   ToPrecision(230.0, 2) -> "230"
   //   ToPrecision(230.0, 2) -> "230."  with EMIT_TRAILING_DECIMAL_POINT.
   //   ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT.
+  //
+  // The min_exponent_width is used for exponential representations.
+  // The converter adds leading '0's to the exponent until the exponent
+  // is at least min_exponent_width digits long.
+  // The min_exponent_width is clamped to 5.
+  // As such, the exponent may never have more than 5 digits in total.
   DoubleToStringConverter(int flags,
                           const char* infinity_symbol,
                           const char* nan_symbol,
@@ -111,7 +117,8 @@
                           int decimal_in_shortest_low,
                           int decimal_in_shortest_high,
                           int max_leading_padding_zeroes_in_precision_mode,
-                          int max_trailing_padding_zeroes_in_precision_mode)
+                          int max_trailing_padding_zeroes_in_precision_mode,
+                          int min_exponent_width = 0)
       : flags_(flags),
         infinity_symbol_(infinity_symbol),
         nan_symbol_(nan_symbol),
@@ -121,7 +128,8 @@
         max_leading_padding_zeroes_in_precision_mode_(
             max_leading_padding_zeroes_in_precision_mode),
         max_trailing_padding_zeroes_in_precision_mode_(
-            max_trailing_padding_zeroes_in_precision_mode) {
+            max_trailing_padding_zeroes_in_precision_mode),
+        min_exponent_width_(min_exponent_width) {
     // When 'trailing zero after the point' is set, then 'trailing point'
     // must be set too.
     DOUBLE_CONVERSION_ASSERT(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) ||
@@ -378,6 +386,7 @@
   const int decimal_in_shortest_high_;
   const int max_leading_padding_zeroes_in_precision_mode_;
   const int max_trailing_padding_zeroes_in_precision_mode_;
+  const int min_exponent_width_;
 
   DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter);
 };
diff --git a/test/cctest/test-conversions.cc b/test/cctest/test-conversions.cc
index 397d8b3..e723c95 100644
--- a/test/cctest/test-conversions.cc
+++ b/test/cctest/test-conversions.cc
@@ -71,6 +71,90 @@
   CHECK(dc.ToShortest(-0.0, &builder));
   CHECK_EQ("0", builder.Finalize());
 
+  // Test min_exponent_width
+  flags = DoubleToStringConverter::UNIQUE_ZERO |
+      DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN;
+  DoubleToStringConverter dcExpWidth2(flags, NULL, NULL, 'e', -4, 6, 0, 0, 2);
+
+  builder.Reset();
+  CHECK(dcExpWidth2.ToShortest(11111111111.0, &builder));
+  CHECK_EQ("1.1111111111e+10", builder.Finalize());
+
+  builder.Reset();
+  CHECK(dcExpWidth2.ToShortest(1111111111.0, &builder));
+  CHECK_EQ("1.111111111e+09", builder.Finalize());
+
+  builder.Reset();
+  CHECK(dcExpWidth2.ToShortest(1111111.0, &builder));
+  CHECK_EQ("1.111111e+06", builder.Finalize());
+
+  builder.Reset();
+  CHECK(dcExpWidth2.ToShortest(111111.0, &builder));
+  CHECK_EQ("111111", builder.Finalize());
+
+  builder.Reset();
+  CHECK(dcExpWidth2.ToShortest(10000000000.0, &builder));
+  CHECK_EQ("1e+10", builder.Finalize());
+
+  builder.Reset();
+  CHECK(dcExpWidth2.ToShortest(1000000000.0, &builder));
+  CHECK_EQ("1e+09", builder.Finalize());
+
+  DoubleToStringConverter dcExpWidth0(flags, NULL, NULL, 'e', -4, 6, 0, 0, 0);
+
+  builder.Reset();
+  CHECK(dcExpWidth0.ToShortest(11111111111.0, &builder));
+  CHECK_EQ("1.1111111111e+10", builder.Finalize());
+
+  builder.Reset();
+  CHECK(dcExpWidth0.ToShortest(1111111111.0, &builder));
+  CHECK_EQ("1.111111111e+9", builder.Finalize());
+
+  builder.Reset();
+  CHECK(dcExpWidth0.ToShortest(1111111.0, &builder));
+  CHECK_EQ("1.111111e+6", builder.Finalize());
+
+  builder.Reset();
+  CHECK(dcExpWidth0.ToShortest(111111.0, &builder));
+  CHECK_EQ("111111", builder.Finalize());
+
+  builder.Reset();
+  CHECK(dcExpWidth0.ToShortest(10000000000.0, &builder));
+  CHECK_EQ("1e+10", builder.Finalize());
+
+  builder.Reset();
+  CHECK(dcExpWidth0.ToShortest(1000000000.0, &builder));
+  CHECK_EQ("1e+9", builder.Finalize());
+
+  // Set min_exponent_width to 100 is equal to 5,
+  // as kMaxExponentLength is defined to 5 in double-to-string.cc
+  DoubleToStringConverter dcExpWidth100(flags, NULL, NULL, 'e', -4, 6, 0, 0, 100);
+
+  builder.Reset();
+  CHECK(dcExpWidth100.ToShortest(11111111111.0, &builder));
+  CHECK_EQ("1.1111111111e+00010", builder.Finalize());
+
+  builder.Reset();
+  CHECK(dcExpWidth100.ToShortest(1111111111.0, &builder));
+  CHECK_EQ("1.111111111e+00009", builder.Finalize());
+
+  builder.Reset();
+  CHECK(dcExpWidth100.ToShortest(1111111.0, &builder));
+  CHECK_EQ("1.111111e+00006", builder.Finalize());
+
+  builder.Reset();
+  CHECK(dcExpWidth100.ToShortest(111111.0, &builder));
+  CHECK_EQ("111111", builder.Finalize());
+
+  builder.Reset();
+  CHECK(dcExpWidth100.ToShortest(10000000000.0, &builder));
+  CHECK_EQ("1e+00010", builder.Finalize());
+
+  builder.Reset();
+  CHECK(dcExpWidth100.ToShortest(1000000000.0, &builder));
+  CHECK_EQ("1e+00009", builder.Finalize());
+  // End of min_exponent_width testing
+
   flags = DoubleToStringConverter::NO_FLAGS;
   DoubleToStringConverter dc2(flags, NULL, NULL, 'e', -1, 1, 0, 0);
   builder.Reset();