diff --git a/include/nlohmann/detail/conversions/to_chars.hpp b/include/nlohmann/detail/conversions/to_chars.hpp index e10741c92..7f6c4cc4b 100644 --- a/include/nlohmann/detail/conversions/to_chars.hpp +++ b/include/nlohmann/detail/conversions/to_chars.hpp @@ -978,10 +978,11 @@ notation. Otherwise it will be printed in exponential notation. JSON_HEDLEY_NON_NULL(1) JSON_HEDLEY_RETURNS_NON_NULL inline char* format_buffer(char* buf, int len, int decimal_exponent, - int min_exp, int max_exp) + int min_exp, int max_exp, size_t precision) { JSON_ASSERT(min_exp < 0); JSON_ASSERT(max_exp > 0); + precision = (std::min)(precision, std::numeric_limits::max_digits10); const int k = len; const int n = len + decimal_exponent; @@ -1009,6 +1010,9 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent, JSON_ASSERT(k > n); + // truncate the digits by the precision + k = (std::min)(static_cast(n) + precision, static_cast(k)); + std::memmove(buf + (static_cast(n) + 1), buf + n, static_cast(k) - static_cast(n)); buf[n] = '.'; return buf + (static_cast(k) + 1U); @@ -1023,7 +1027,8 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent, buf[0] = '0'; buf[1] = '.'; std::memset(buf + 2, '0', static_cast(-n)); - return buf + (2U + static_cast(-n) + static_cast(k)); + // truncate the reported buffer end by the precision + return buf + (std::min)(precision + 2, (2U + static_cast(-n) + static_cast(k))); } if (k == 1) @@ -1040,7 +1045,7 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent, std::memmove(buf + 2, buf + 1, static_cast(k) - 1); buf[1] = '.'; - buf += 1 + static_cast(k); + buf += 1 + (std::min)(precision, static_cast(k)); } *buf++ = 'e'; @@ -1062,7 +1067,7 @@ format. Returns an iterator pointing past-the-end of the decimal representation. template JSON_HEDLEY_NON_NULL(1, 2) JSON_HEDLEY_RETURNS_NON_NULL -char* to_chars(char* first, const char* last, FloatType value) +char* to_chars(char* first, const char* last, FloatType value, size_t precision = std::numeric_limits::max_digits10) { static_cast(last); // maybe unused - fix warning JSON_ASSERT(std::isfinite(value)); @@ -1111,7 +1116,7 @@ char* to_chars(char* first, const char* last, FloatType value) JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); JSON_ASSERT(last - first >= std::numeric_limits::max_digits10 + 6); - return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); + return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp, precision); } } // namespace detail diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index ed20b0d9e..36d4dca87 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -64,13 +64,14 @@ class serializer @param[in] ichar indentation character to use @param[in] error_handler_ how to react on decoding errors */ - serializer(output_adapter_t s, const char ichar, + serializer(output_adapter_t s, const char ichar, std::size_t prec = 1000, error_handler_t error_handler_ = error_handler_t::strict) : o(std::move(s)) , loc(std::localeconv()) , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->thousands_sep))) , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->decimal_point))) , indent_char(ichar) + , precision(prec) , indent_string(512, indent_char) , error_handler(error_handler_) {} @@ -820,7 +821,7 @@ class serializer void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/) { auto* begin = number_buffer.data(); - auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x); + auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x, precision); o->write_characters(begin, static_cast(end - begin)); } @@ -828,10 +829,10 @@ class serializer void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/) { // get number of digits for a float -> text -> float round-trip - static constexpr auto d = std::numeric_limits::max_digits10; + static constexpr int d_max = std::numeric_limits::max_digits10; + int d = static_cast((std::min)(precision, static_cast(d_max))); // the actual conversion - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x); // negative value indicates an error @@ -977,6 +978,8 @@ class serializer /// the indentation character const char indent_char; + /// precision for floating point output + std::size_t precision; /// the indentation string string_t indent_string; diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 31ca64539..47f271ba4 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1272,10 +1272,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec string_t dump(const int indent = -1, const char indent_char = ' ', const bool ensure_ascii = false, - const error_handler_t error_handler = error_handler_t::strict) const + const error_handler_t error_handler = error_handler_t::strict, + const size_t precision = std::numeric_limits::max()) const { string_t result; - serializer s(detail::output_adapter(result), indent_char, error_handler); + serializer s(detail::output_adapter(result), indent_char, precision, error_handler); if (indent >= 0) { @@ -3982,8 +3983,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec o.width(0); // do the actual serialization - serializer s(detail::output_adapter(o), o.fill()); + serializer s(detail::output_adapter(o), o.fill(), static_cast(o.precision())).; s.dump(j, pretty_print, false, static_cast(indentation)); + return o; }