Support trailing decimals for floats
authorTom Jackson <tjackson@fb.com>
Thu, 24 Jul 2014 00:19:47 +0000 (17:19 -0700)
committerChip Turner <chip@fb.com>
Fri, 25 Jul 2014 16:07:09 +0000 (09:07 -0700)
Summary: So integer-like numbers can be formatted in a manner which disambiguates them from integers.

Test Plan: Unit tests

Reviewed By: tudorb@fb.com

Subscribers: jfh, cscheau

FB internal diff: D1454446

folly/Format-inl.h
folly/Format.cpp
folly/FormatArg.h
folly/docs/Format.md
folly/test/FormatTest.cpp

index 77248b79dad43f62d208cb17d7fe19524bc766c1..f9de120081597f9a0a53327dea8108f045755a72 100644 (file)
@@ -649,6 +649,11 @@ class FormatValue<double> {
       break;
     };
 
+    auto flags =
+        DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN |
+        (arg.trailingDot ? DoubleToStringConverter::EMIT_TRAILING_DECIMAL_POINT
+                         : 0);
+
     double val = val_;
     switch (arg.presentation) {
     case '%':
@@ -660,13 +665,14 @@ class FormatValue<double> {
             DoubleToStringConverter::kMaxFixedDigitsAfterPoint) {
           arg.precision = DoubleToStringConverter::kMaxFixedDigitsAfterPoint;
         }
-        DoubleToStringConverter conv(
-            DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN,
-            infinitySymbol,
-            nanSymbol,
-            exponentSymbol,
-            -4, arg.precision,
-            0, 0);
+        DoubleToStringConverter conv(flags,
+                                     infinitySymbol,
+                                     nanSymbol,
+                                     exponentSymbol,
+                                     -4,
+                                     arg.precision,
+                                     0,
+                                     0);
         arg.enforce(conv.ToFixed(val, arg.precision, &builder),
                     "fixed double conversion failed");
       }
@@ -678,13 +684,14 @@ class FormatValue<double> {
           arg.precision = DoubleToStringConverter::kMaxExponentialDigits;
         }
 
-        DoubleToStringConverter conv(
-            DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN,
-            infinitySymbol,
-            nanSymbol,
-            exponentSymbol,
-            -4, arg.precision,
-            0, 0);
+        DoubleToStringConverter conv(flags,
+                                     infinitySymbol,
+                                     nanSymbol,
+                                     exponentSymbol,
+                                     -4,
+                                     arg.precision,
+                                     0,
+                                     0);
         arg.enforce(conv.ToExponential(val, arg.precision, &builder));
       }
       break;
@@ -698,13 +705,14 @@ class FormatValue<double> {
                    DoubleToStringConverter::kMaxPrecisionDigits) {
           arg.precision = DoubleToStringConverter::kMaxPrecisionDigits;
         }
-        DoubleToStringConverter conv(
-            DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN,
-            infinitySymbol,
-            nanSymbol,
-            exponentSymbol,
-            -4, arg.precision,
-            0, 0);
+        DoubleToStringConverter conv(flags,
+                                     infinitySymbol,
+                                     nanSymbol,
+                                     exponentSymbol,
+                                     -4,
+                                     arg.precision,
+                                     0,
+                                     0);
         arg.enforce(conv.ToShortest(val, &builder));
       }
       break;
index 53a218622d812a7bb6fe63b2baf5e99d0e5b4682..feec8fb255c3f9b4c15807e22f43e9433aad56d8 100644 (file)
@@ -96,7 +96,15 @@ void FormatArg::initSlow() {
       while (p != end && *p >= '0' && *p <= '9') {
         ++p;
       }
-      precision = to<int>(StringPiece(b, p));
+      if (p != b) {
+        precision = to<int>(StringPiece(b, p));
+        if (p != end && *p == '.') {
+          trailingDot = true;
+          ++p;
+        }
+      } else {
+        trailingDot = true;
+      }
 
       if (p == end) return;
     }
index e815064660d0c9fbc89be0b15b2f8acf740fe414..b6a00187001ed7fe180aa16ac79c2aa44722f0cd 100644 (file)
@@ -46,6 +46,7 @@ struct FormatArg {
       sign(Sign::DEFAULT),
       basePrefix(false),
       thousandsSeparator(false),
+      trailingDot(false),
       width(kDefaultWidth),
       precision(kDefaultPrecision),
       presentation(kDefaultPresentation),
@@ -128,6 +129,11 @@ struct FormatArg {
    */
   bool thousandsSeparator;
 
+  /**
+   * Force a trailing decimal on doubles which could be rendered as ints
+   */
+  bool trailingDot;
+
   /**
    * Field width
    */
index 5696c8be0b28d150201c24d1a8a5e38f12de91d4..012f0a28a3eac8fd9501451aa25b9e91e0041e77 100644 (file)
@@ -124,7 +124,7 @@ Format string (`vformat`):
 - `format_spec`: format specification, see below
 
 Format specification:
-`[[fill] align] [sign] ["#"] ["0"] [width] [","] ["." precision] [type]`
+`[[fill] align] [sign] ["#"] ["0"] [width] [","] ["." precision] ["."] [type]`
 
 - `fill` (may only be specified if `align` is also specified): pad with this
   character ('` `' (space) or '`0`' (zero) might be useful; space is default)
@@ -149,6 +149,8 @@ Format specification:
     - for floating point values, number of digits after decimal point ('`f`' or
       '`F`' presentation) or number of significant digits ('`g`' or '`G`')
     - for others, maximum field size (truncate subsequent characters)
+- '`.`' (when used after precision or in lieu of precison): Forces a trailing
+  decimal point to make it clear this is a floating point value.
 - `type`: presentation format, see below
 
 Presentation formats:
index 8db3f09d2d6b9d8cccc2639b6938b5b9d191d28b..448732730243661d78971e6f815c34e8baec0298 100644 (file)
@@ -239,6 +239,13 @@ TEST(Format, Float) {
   EXPECT_EQ("0.10", sformat("{:.2f}", 0.1));
   EXPECT_EQ("0.01", sformat("{:.2f}", 0.01));
   EXPECT_EQ("0.00", sformat("{:.2f}", 0.001));
+
+  EXPECT_EQ("100000.", sformat("{:.}", 100000.0));
+  EXPECT_EQ("1e+6", sformat("{:.}", 1000000.0));
+  EXPECT_EQ(" 100000.", sformat("{:8.}", 100000.0));
+  EXPECT_EQ("100000.", sformat("{:4.}", 100000.0));
+  EXPECT_EQ("  100000", sformat("{:8.8}", 100000.0));
+  EXPECT_EQ(" 100000.", sformat("{:8.8.}", 100000.0));
 }
 
 TEST(Format, MultiLevel) {