Add setting precision for json writers and also add decimal places precision type. (#752)

* Added setting precision for writers.
* Added special case for precise precision and global precision.
* Added good setting of type of precision and also added this type to BuiltStreamWriter and for its settings.
* Added some tests.
diff --git a/include/json/value.h b/include/json/value.h
index 2643782..3474cd8 100644
--- a/include/json/value.h
+++ b/include/json/value.h
@@ -109,6 +109,13 @@
   numberOfCommentPlacement
 };
 
+/** \brief Type of precision for formatting of real values.
+ */
+enum PrecisionType {
+  significantDigits = 0, ///< we set max number of significant digits in string
+  decimalPlaces          ///< we set max number of digits after "." in string
+};
+
 //# ifdef JSON_USE_CPPTL
 //   typedef CppTL::AnyEnumerator<const char *> EnumMemberNames;
 //   typedef CppTL::AnyEnumerator<const Value &> EnumValues;
@@ -220,6 +227,9 @@
   static const UInt64 maxUInt64;
 #endif // defined(JSON_HAS_INT64)
 
+  /// Default precision for real value for string representation.
+  static const UInt defaultRealPrecision;
+
 // Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler
 // when using gcc and clang backend compilers.  CZString
 // cannot be defined as private.  See issue #486
diff --git a/include/json/writer.h b/include/json/writer.h
index 35ec396..767b3bd 100644
--- a/include/json/writer.h
+++ b/include/json/writer.h
@@ -106,6 +106,10 @@
       - If true, outputs non-finite floating point values in the following way:
         NaN values as "NaN", positive infinity as "Infinity", and negative infinity
         as "-Infinity".
+    - "precision": int
+      - Number of precision digits for formatting of real values.
+    - "precisionType": "significant"(default) or "decimal"
+      - Type of precision for formatting of real values.
 
     You can examine 'settings_` yourself
     to see the defaults. You can also write and read them just like any
@@ -339,7 +343,8 @@
 #endif // if defined(JSON_HAS_INT64)
 JSONCPP_STRING JSON_API valueToString(LargestInt value);
 JSONCPP_STRING JSON_API valueToString(LargestUInt value);
-JSONCPP_STRING JSON_API valueToString(double value);
+JSONCPP_STRING JSON_API valueToString(double value, unsigned int precision = Value::defaultRealPrecision,
+                                      PrecisionType precisionType = PrecisionType::significantDigits);
 JSONCPP_STRING JSON_API valueToString(bool value);
 JSONCPP_STRING JSON_API valueToQuotedString(const char* value);
 
diff --git a/src/lib_json/json_tool.h b/src/lib_json/json_tool.h
index 4590bfc..86f1f2c 100644
--- a/src/lib_json/json_tool.h
+++ b/src/lib_json/json_tool.h
@@ -109,6 +109,20 @@
   }
 }
 
+/**
+ * Delete zeros in the end of string, if it isn't last zero before '.' character.
+ */
+static inline void fixZerosInTheEnd(char* begin, char* end) {
+  end--;
+  while ((begin < end) && (*end == '0')) {
+    // don't delete last zero before point.
+    if (*(end - 1) != '.') {
+      *end = '\0';
+    }
+    end--;
+  }
+}
+
 } // namespace Json {
 
 #endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED
diff --git a/src/lib_json/json_value.cpp b/src/lib_json/json_value.cpp
index 1633f49..ed49df2 100644
--- a/src/lib_json/json_value.cpp
+++ b/src/lib_json/json_value.cpp
@@ -64,6 +64,8 @@
 const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2);
 const LargestUInt Value::maxLargestUInt = LargestUInt(-1);
 
+const UInt Value::defaultRealPrecision = 17;
+
 #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
 template <typename T, typename U>
 static inline bool InRange(double d, T min, U max) {
diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp
index 5167c31..be03a66 100644
--- a/src/lib_json/json_writer.cpp
+++ b/src/lib_json/json_writer.cpp
@@ -118,14 +118,18 @@
 #endif // # if defined(JSON_HAS_INT64)
 
 namespace {
-JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision) {
+JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision, PrecisionType precisionType) {
   // Allocate a buffer that is more than large enough to store the 16 digits of
   // precision requested below.
   char buffer[36];
   int len = -1;
 
   char formatString[15];
-  snprintf(formatString, sizeof(formatString), "%%.%ug", precision);
+  if (precisionType == PrecisionType::significantDigits) {
+    snprintf(formatString, sizeof(formatString), "%%.%ug", precision);
+  } else {
+    snprintf(formatString, sizeof(formatString), "%%.%uf", precision);
+  }
 
   // Print into the buffer. We need not request the alternative representation
   // that always has a decimal point because JSON doesn't distinguish the
@@ -133,6 +137,10 @@
   if (isfinite(value)) {
     len = snprintf(buffer, sizeof(buffer), formatString, value);
     fixNumericLocale(buffer, buffer + len);
+    // to delete use-less too much zeros in the end of string
+    if (precisionType == PrecisionType::decimalPlaces) {
+      fixZerosInTheEnd(buffer, buffer + len);
+    }
 
     // try to ensure we preserve the fact that this was given to us as a double on input
     if (!strchr(buffer, '.') && !strchr(buffer, 'e')) {
@@ -154,7 +162,9 @@
 }
 }
 
-JSONCPP_STRING valueToString(double value) { return valueToString(value, false, 17); }
+JSONCPP_STRING valueToString(double value, unsigned int precision, PrecisionType precisionType) {
+  return valueToString(value, false, precision, precisionType);
+}
 
 JSONCPP_STRING valueToString(bool value) { return value ? "true" : "false"; }
 
@@ -856,7 +866,8 @@
       JSONCPP_STRING const& nullSymbol,
       JSONCPP_STRING const& endingLineFeedSymbol,
       bool useSpecialFloats,
-      unsigned int precision);
+      unsigned int precision,
+      PrecisionType precisionType);
   int write(Value const& root, JSONCPP_OSTREAM* sout) JSONCPP_OVERRIDE;
 private:
   void writeValue(Value const& value);
@@ -885,6 +896,7 @@
   bool indented_ : 1;
   bool useSpecialFloats_ : 1;
   unsigned int precision_;
+  PrecisionType precisionType_;
 };
 BuiltStyledStreamWriter::BuiltStyledStreamWriter(
       JSONCPP_STRING const& indentation,
@@ -893,7 +905,8 @@
       JSONCPP_STRING const& nullSymbol,
       JSONCPP_STRING const& endingLineFeedSymbol,
       bool useSpecialFloats,
-      unsigned int precision)
+      unsigned int precision,
+      PrecisionType precisionType)
   : rightMargin_(74)
   , indentation_(indentation)
   , cs_(cs)
@@ -904,6 +917,7 @@
   , indented_(false)
   , useSpecialFloats_(useSpecialFloats)
   , precision_(precision)
+  , precisionType_(precisionType)
 {
 }
 int BuiltStyledStreamWriter::write(Value const& root, JSONCPP_OSTREAM* sout)
@@ -933,7 +947,7 @@
     pushValue(valueToString(value.asLargestUInt()));
     break;
   case realValue:
-    pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_));
+    pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_, precisionType_));
     break;
   case stringValue:
   {
@@ -1145,6 +1159,7 @@
 {
   JSONCPP_STRING indentation = settings_["indentation"].asString();
   JSONCPP_STRING cs_str = settings_["commentStyle"].asString();
+  JSONCPP_STRING pt_str = settings_["precisionType"].asString();
   bool eyc = settings_["enableYAMLCompatibility"].asBool();
   bool dnp = settings_["dropNullPlaceholders"].asBool();
   bool usf = settings_["useSpecialFloats"].asBool(); 
@@ -1157,6 +1172,14 @@
   } else {
     throwRuntimeError("commentStyle must be 'All' or 'None'");
   }
+  PrecisionType precisionType(significantDigits);
+  if (pt_str == "significant") {
+    precisionType = PrecisionType::significantDigits;
+  } else if (pt_str == "decimal") {
+    precisionType = PrecisionType::decimalPlaces;
+  } else {
+    throwRuntimeError("precisionType must be 'significant' or 'decimal'");
+  }
   JSONCPP_STRING colonSymbol = " : ";
   if (eyc) {
     colonSymbol = ": ";
@@ -1171,7 +1194,7 @@
   JSONCPP_STRING endingLineFeedSymbol;
   return new BuiltStyledStreamWriter(
       indentation, cs,
-      colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre);
+      colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre, precisionType);
 }
 static void getValidWriterKeys(std::set<JSONCPP_STRING>* valid_keys)
 {
@@ -1182,6 +1205,7 @@
   valid_keys->insert("dropNullPlaceholders");
   valid_keys->insert("useSpecialFloats");
   valid_keys->insert("precision");
+  valid_keys->insert("precisionType");
 }
 bool StreamWriterBuilder::validate(Json::Value* invalid) const
 {
@@ -1214,6 +1238,7 @@
   (*settings)["dropNullPlaceholders"] = false;
   (*settings)["useSpecialFloats"] = false;
   (*settings)["precision"] = 17;
+  (*settings)["precisionType"] = "significant";
   //! [StreamWriterBuilderDefaults]
 }
 
diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp
index ee81790..cbf4823 100644
--- a/src/test_lib_json/main.cpp
+++ b/src/test_lib_json/main.cpp
@@ -1757,6 +1757,27 @@
     expected = "0.25634569487374054";
     result = Json::writeString(b, v);
     JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+
+    b.settings_["precision"] = 5;
+    b.settings_["precisionType"] = "decimal";
+    v = 0.256345694873740545068;
+    expected = "0.25635";
+    result = Json::writeString(b, v);
+    JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+
+    b.settings_["precision"] = 1;
+    b.settings_["precisionType"] = "decimal";
+    v = 0.256345694873740545068;
+    expected = "0.3";
+    result = Json::writeString(b, v);
+    JSONTEST_ASSERT_STRING_EQUAL(expected, result);
+
+    b.settings_["precision"] = 10;
+    b.settings_["precisionType"] = "decimal";
+    v = 0.23300000;
+    expected = "0.233";
+    result = Json::writeString(b, v);
+    JSONTEST_ASSERT_STRING_EQUAL(expected, result);
 }
 
 struct WriterTest : JsonTest::TestCase {};