Skip to content

Commit

Permalink
implement settable floating point precision output
Browse files Browse the repository at this point in the history
Signed-off-by: Daniel Oberhoff <[email protected]>
  • Loading branch information
maddanio committed Jan 10, 2025
1 parent 8c391e0 commit a509d42
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 12 deletions.
15 changes: 10 additions & 5 deletions include/nlohmann/detail/conversions/to_chars.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)<size_t>(precision, std::numeric_limits<FloatType>::max_digits10);

const int k = len;
const int n = len + decimal_exponent;
Expand Down Expand Up @@ -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<size_t>(n) + precision, static_cast<size_t>(k));

std::memmove(buf + (static_cast<size_t>(n) + 1), buf + n, static_cast<size_t>(k) - static_cast<size_t>(n));
buf[n] = '.';
return buf + (static_cast<size_t>(k) + 1U);
Expand All @@ -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<size_t>(-n));
return buf + (2U + static_cast<size_t>(-n) + static_cast<size_t>(k));
// truncate the reported buffer end by the precision
return buf + (std::min)(precision + 2, (2U + static_cast<size_t>(-n) + static_cast<size_t>(k)));
}

if (k == 1)
Expand All @@ -1040,7 +1045,7 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent,

std::memmove(buf + 2, buf + 1, static_cast<size_t>(k) - 1);
buf[1] = '.';
buf += 1 + static_cast<size_t>(k);
buf += 1 + (std::min)(precision, static_cast<size_t>(k));
}

*buf++ = 'e';
Expand All @@ -1062,7 +1067,7 @@ format. Returns an iterator pointing past-the-end of the decimal representation.
template<typename FloatType>
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<FloatType>::max_digits10)
{
static_cast<void>(last); // maybe unused - fix warning
JSON_ASSERT(std::isfinite(value));
Expand Down Expand Up @@ -1111,7 +1116,7 @@ char* to_chars(char* first, const char* last, FloatType value)
JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::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
Expand Down
11 changes: 7 additions & 4 deletions include/nlohmann/detail/output/serializer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<char> s, const char ichar,
serializer(output_adapter_t<char> 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<char>::to_char_type(* (loc->thousands_sep)))
, decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))
, indent_char(ichar)
, precision(prec)
, indent_string(512, indent_char)
, error_handler(error_handler_)
{}
Expand Down Expand Up @@ -820,18 +821,18 @@ 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<size_t>(end - begin));
}

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<number_float_t>::max_digits10;
static constexpr int d_max = std::numeric_limits<number_float_t>::max_digits10;
int d = static_cast<int>((std::min)(precision, static_cast<std::size_t>(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
Expand Down Expand Up @@ -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;

Expand Down
8 changes: 5 additions & 3 deletions include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<size_t>::max()) const
{
string_t result;
serializer s(detail::output_adapter<char, string_t>(result), indent_char, error_handler);
serializer s(detail::output_adapter<char, string_t>(result), indent_char, precision, error_handler);

if (indent >= 0)
{
Expand Down Expand Up @@ -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<char>(o), o.fill());
serializer s(detail::output_adapter<char>(o), o.fill(), static_cast<size_t>(o.precision())).;
s.dump(j, pretty_print, false, static_cast<unsigned int>(indentation));

return o;
}

Expand Down

0 comments on commit a509d42

Please sign in to comment.