From 1286d35767448925db9ac1d6d52855da822ce2fe Mon Sep 17 00:00:00 2001
From: Niels <niels.lohmann@gmail.com>
Date: Wed, 20 Jul 2016 20:07:45 +0200
Subject: [PATCH] make sure precision is reset on output streams during
 serialization

---
 src/json.hpp      | 31 ++++++++++++++++++++++---------
 src/json.hpp.re2c | 31 ++++++++++++++++++++++---------
 test/src/unit.cpp | 22 ++++++++++++++++++++++
 3 files changed, 66 insertions(+), 18 deletions(-)

diff --git a/src/json.hpp b/src/json.hpp
index fa14b0e7..29155327 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -2120,6 +2120,12 @@ class basic_json
         // fix locale problems
         ss.imbue(std::locale(std::locale(), new DecimalSeparator));
 
+        // 6, 15 or 16 digits of precision allows round-trip IEEE 754
+        // string->float->string, string->double->string or string->long
+        // double->string; to be safe, we read this value from
+        // std::numeric_limits<number_float_t>::digits10
+        ss.precision(std::numeric_limits<double>::digits10);
+
         if (indent >= 0)
         {
             dump(ss, true, static_cast<unsigned int>(indent));
@@ -5693,6 +5699,10 @@ class basic_json
     `std::setw(4)` on @a o sets the indentation level to `4` and the
     serialization result is the same as calling `dump(4)`.
 
+    @note During serializaion, the locale and the precision of the output
+    stream @a o are changed. The original values are restored when the
+    function returns.
+
     @param[in,out] o  stream to serialize to
     @param[in] j  JSON value to serialize
 
@@ -5713,14 +5723,23 @@ class basic_json
 
         // reset width to 0 for subsequent calls to this stream
         o.width(0);
+
         // fix locale problems
-        auto old_locale = o.imbue(std::locale(std::locale(), new DecimalSeparator));
+        const auto old_locale = o.imbue(std::locale(std::locale(), new DecimalSeparator));
+        // set precision
+
+        // 6, 15 or 16 digits of precision allows round-trip IEEE 754
+        // string->float->string, string->double->string or string->long
+        // double->string; to be safe, we read this value from
+        // std::numeric_limits<number_float_t>::digits10
+        const auto old_preicison = o.precision(std::numeric_limits<double>::digits10);
 
         // do the actual serialization
         j.dump(o, pretty_print, static_cast<unsigned int>(indentation));
 
-        // reset locale
+        // reset locale and precision
         o.imbue(old_locale);
+        o.precision(old_preicison);
         return o;
     }
 
@@ -6184,13 +6203,7 @@ class basic_json
                 }
                 else
                 {
-                    // Otherwise 6, 15 or 16 digits of precision allows
-                    // round-trip IEEE 754 string->float->string,
-                    // string->double->string or string->long
-                    // double->string; to be safe, we read this value from
-                    // std::numeric_limits<number_float_t>::digits10
-                    o << std::setprecision(std::numeric_limits<double>::digits10)
-                      << m_value.number_float;
+                    o << m_value.number_float;
                 }
                 return;
             }
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index dd1bda1f..8eeedefe 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -2120,6 +2120,12 @@ class basic_json
         // fix locale problems
         ss.imbue(std::locale(std::locale(), new DecimalSeparator));
 
+        // 6, 15 or 16 digits of precision allows round-trip IEEE 754
+        // string->float->string, string->double->string or string->long
+        // double->string; to be safe, we read this value from
+        // std::numeric_limits<number_float_t>::digits10
+        ss.precision(std::numeric_limits<double>::digits10);
+
         if (indent >= 0)
         {
             dump(ss, true, static_cast<unsigned int>(indent));
@@ -5693,6 +5699,10 @@ class basic_json
     `std::setw(4)` on @a o sets the indentation level to `4` and the
     serialization result is the same as calling `dump(4)`.
 
+    @note During serializaion, the locale and the precision of the output
+    stream @a o are changed. The original values are restored when the
+    function returns.
+
     @param[in,out] o  stream to serialize to
     @param[in] j  JSON value to serialize
 
@@ -5713,14 +5723,23 @@ class basic_json
 
         // reset width to 0 for subsequent calls to this stream
         o.width(0);
+
         // fix locale problems
-        auto old_locale = o.imbue(std::locale(std::locale(), new DecimalSeparator));
+        const auto old_locale = o.imbue(std::locale(std::locale(), new DecimalSeparator));
+        // set precision
+
+        // 6, 15 or 16 digits of precision allows round-trip IEEE 754
+        // string->float->string, string->double->string or string->long
+        // double->string; to be safe, we read this value from
+        // std::numeric_limits<number_float_t>::digits10
+        const auto old_preicison = o.precision(std::numeric_limits<double>::digits10);
 
         // do the actual serialization
         j.dump(o, pretty_print, static_cast<unsigned int>(indentation));
 
-        // reset locale
+        // reset locale and precision
         o.imbue(old_locale);
+        o.precision(old_preicison);
         return o;
     }
 
@@ -6184,13 +6203,7 @@ class basic_json
                 }
                 else
                 {
-                    // Otherwise 6, 15 or 16 digits of precision allows
-                    // round-trip IEEE 754 string->float->string,
-                    // string->double->string or string->long
-                    // double->string; to be safe, we read this value from
-                    // std::numeric_limits<number_float_t>::digits10
-                    o << std::setprecision(std::numeric_limits<double>::digits10)
-                      << m_value.number_float;
+                    o << m_value.number_float;
                 }
                 return;
             }
diff --git a/test/src/unit.cpp b/test/src/unit.cpp
index a12f0529..a7ca7394 100644
--- a/test/src/unit.cpp
+++ b/test/src/unit.cpp
@@ -1685,6 +1685,28 @@ TEST_CASE("object inspection")
             json j_discarded(json::value_t::discarded);
             CHECK(j_discarded.dump() == "<discarded>");
         }
+
+        SECTION("check that precision is reset after serialization")
+        {
+            // create stringstream and set precision
+            std::stringstream ss;
+            ss.precision(3);
+            ss << 3.141592653589793 << std::fixed;
+            CHECK(ss.str() == "3.14");
+
+            // reset stringstream
+            ss.str(std::string());
+
+            // use stringstream for JSON serialization
+            json j_number = 3.141592653589793;
+            ss << j_number;
+
+            // check that precision has been overridden during serialization
+            CHECK(ss.str() == "3.141592653589793");
+
+            // check that precision has been restored
+            CHECK(ss.precision() == 3);
+        }
     }
 
     SECTION("return the type of the object (explicit)")