From 1286d35767448925db9ac1d6d52855da822ce2fe Mon Sep 17 00:00:00 2001 From: Niels 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::digits10 + ss.precision(std::numeric_limits::digits10); + if (indent >= 0) { dump(ss, true, static_cast(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::digits10 + const auto old_preicison = o.precision(std::numeric_limits::digits10); // do the actual serialization j.dump(o, pretty_print, static_cast(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::digits10 - o << std::setprecision(std::numeric_limits::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::digits10 + ss.precision(std::numeric_limits::digits10); + if (indent >= 0) { dump(ss, true, static_cast(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::digits10 + const auto old_preicison = o.precision(std::numeric_limits::digits10); // do the actual serialization j.dump(o, pretty_print, static_cast(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::digits10 - o << std::setprecision(std::numeric_limits::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() == ""); } + + 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)")