added toml::format_flags::relaxed_float_precision

This commit is contained in:
Mark Gillard 2022-01-08 16:24:59 +02:00
parent f6ce95907e
commit cdf85a9b60
10 changed files with 97 additions and 56 deletions

View File

@ -59,6 +59,7 @@ Highlights are indicated with ❤️.
- added `toml::format_flags::allow_unicode_strings`
- added `toml::format_flags::indent_array_elements` (#120) (@W4RH4WK)
- added `toml::format_flags::indent_sub_tables` (#120) (@W4RH4WK)
- added `toml::format_flags::relaxed_float_precision` (#89) (@vaartis)
- added `toml::format_flags::quote_infinities_and_nans`
- added `toml::is_key<>` and toml::is_key_or_convertible<>` metafunctions
- added `toml::node_view::operator==`

View File

@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14)
project(
tomlplusplus
VERSION 3.0.0
DESCRIPTION "Header-only TOML config file parser and serializer for C++17 (and later!)"
DESCRIPTION "Header-only TOML config file parser and serializer for C++17"
HOMEPAGE_URL "https://marzer.github.io/tomlplusplus/"
LANGUAGES CXX
)

View File

@ -30,7 +30,7 @@ TOML_NAMESPACE_START
/// \eout
///
///
/// \warning Keys in paths are interpreted literally, so whitespace (or lack thereof) matters:
/// \note Keys in paths are interpreted literally, so whitespace (or lack thereof) matters:
/// \cpp
/// toml::at_path(config, "foo.bar") // same as config["foo"]["bar"]
/// toml::at_path(config, "foo. bar") // same as config["foo"][" bar"]

View File

@ -410,7 +410,12 @@ TOML_IMPL_NAMESPACE_START
case fp_class::neg_inf: inf_nan = &constants_->float_neg_inf; break;
case fp_class::pos_inf: inf_nan = &constants_->float_pos_inf; break;
case fp_class::nan: inf_nan = &constants_->float_nan; break;
case fp_class::ok: print_to_stream(*stream_, *val); break;
case fp_class::ok:
print_to_stream(*stream_,
*val,
value_flags::none,
!!(config_.flags & format_flags::relaxed_float_precision));
break;
default: TOML_UNREACHABLE;
}

View File

@ -329,6 +329,13 @@ TOML_NAMESPACE_START // abi namespace
/// \brief Combination mask of all indentation-enabling flags.
indentation = indent_sub_tables | indent_array_elements,
/// \brief Emit floating-point values with relaxed precision.
///
/// \warning Setting this flag may cause serialized documents to no longer round-trip correctly
/// since floats might have a less precise value upon being written out than they did when being
/// read in. Use this flag at your own risk.
relaxed_float_precision = (1ull << 11),
};
TOML_MAKE_FLAGS(format_flags);

View File

@ -516,12 +516,12 @@
//# ATTRIBUTES, UTILITY MACROS ETC
//#====================================================================================================================
#if TOML_GCC || TOML_CLANG || (TOML_ICC && !TOML_ICC_CL)
#if !defined(TOML_FLOAT_CHARCONV) && (TOML_GCC || TOML_CLANG || (TOML_ICC && !TOML_ICC_CL))
// not supported by any version of GCC or Clang as of 26/11/2020
// not supported by any version of ICC on Linux as of 11/01/2021
#define TOML_FLOAT_CHARCONV 0
#endif
#if defined(__EMSCRIPTEN__) || defined(__APPLE__)
#if !defined(TOML_INT_CHARCONV) && (defined(__EMSCRIPTEN__) || defined(__APPLE__))
// causes link errors on emscripten
// causes Mac OS SDK version errors on some versions of Apple Clang
#define TOML_INT_CHARCONV 0
@ -690,9 +690,7 @@
#endif
#define TOML_MAKE_FLAGS_(name, op) \
TOML_NODISCARD \
TOML_ALWAYS_INLINE \
TOML_ATTR(const) \
TOML_CONST_INLINE_GETTER \
constexpr name operator op(name lhs, name rhs) noexcept \
{ \
using under = std::underlying_type_t<name>; \
@ -708,17 +706,13 @@
TOML_MAKE_FLAGS_(name, &); \
TOML_MAKE_FLAGS_(name, |); \
TOML_MAKE_FLAGS_(name, ^); \
TOML_NODISCARD \
TOML_ALWAYS_INLINE \
TOML_ATTR(const) \
TOML_CONST_INLINE_GETTER \
constexpr name operator~(name val) noexcept \
{ \
using under = std::underlying_type_t<name>; \
return static_cast<name>(~static_cast<under>(val)); \
} \
TOML_NODISCARD \
TOML_ALWAYS_INLINE \
TOML_ATTR(const) \
TOML_CONST_INLINE_GETTER \
constexpr bool operator!(name val) noexcept \
{ \
using under = std::underlying_type_t<name>; \

View File

@ -54,10 +54,10 @@ TOML_IMPL_NAMESPACE_START
void print_to_stream(std::ostream&, uint64_t, value_flags = {}, size_t min_digits = 0);
TOML_API
void print_to_stream(std::ostream&, float, value_flags = {});
void print_to_stream(std::ostream&, float, value_flags = {}, bool relaxed_precision = false);
TOML_API
void print_to_stream(std::ostream&, double, value_flags = {});
void print_to_stream(std::ostream&, double, value_flags = {}, bool relaxed_precision = false);
TOML_API
void print_to_stream(std::ostream&, bool);

View File

@ -62,10 +62,10 @@ TOML_ANON_NAMESPACE_START
inline constexpr size_t charconv_buffer_length<uint64_t> = 20; // strlen("18446744073709551615")
template <>
inline constexpr size_t charconv_buffer_length<float> = 40;
inline constexpr size_t charconv_buffer_length<float> = 64;
template <>
inline constexpr size_t charconv_buffer_length<double> = 60;
inline constexpr size_t charconv_buffer_length<double> = 64;
template <typename T>
TOML_INTERNAL_LINKAGE
@ -156,7 +156,10 @@ TOML_ANON_NAMESPACE_START
template <typename T>
TOML_INTERNAL_LINKAGE
void print_floating_point_to_stream(std::ostream & stream, T val, value_flags format = {})
void print_floating_point_to_stream(std::ostream & stream,
T val,
value_flags format,
[[maybe_unused]] bool relaxed_precision)
{
switch (impl::fpclassify(val))
{
@ -178,19 +181,30 @@ TOML_ANON_NAMESPACE_START
#if TOML_FLOAT_CHARCONV
const auto hex = !!(format & value_flags::format_as_hexadecimal);
char buf[charconv_buffer_length<T>];
const auto res = !!(format & value_flags::format_as_hexadecimal)
? std::to_chars(buf, buf + sizeof(buf), val, std::chars_format::hex)
auto res = hex ? std::to_chars(buf, buf + sizeof(buf), val, std::chars_format::hex)
: std::to_chars(buf, buf + sizeof(buf), val);
const auto str = std::string_view{ buf, static_cast<size_t>(res.ptr - buf) };
auto str = std::string_view{ buf, static_cast<size_t>(res.ptr - buf) };
char buf2[charconv_buffer_length<T>];
if (!hex && relaxed_precision)
{
res = std::to_chars(buf2, buf2 + sizeof(buf2), val, std::chars_format::general, 6);
const auto str2 = std::string_view{ buf2, static_cast<size_t>(res.ptr - buf2) };
if (str2.length() < str.length())
str = str2;
}
impl::print_to_stream(stream, str);
if (!(format & value_flags::format_as_hexadecimal) && needs_decimal_point(str))
if (!hex && needs_decimal_point(str))
toml::impl::print_to_stream(stream, ".0"sv);
#else
std::ostringstream ss;
ss.imbue(std::locale::classic());
if (!relaxed_precision)
ss.precision(std::numeric_limits<T>::max_digits10);
if (!!(format & value_flags::format_as_hexadecimal))
ss << std::hexfloat;
@ -286,15 +300,15 @@ TOML_IMPL_NAMESPACE_START
}
TOML_EXTERNAL_LINKAGE
void print_to_stream(std::ostream & stream, float val, value_flags format)
void print_to_stream(std::ostream & stream, float val, value_flags format, bool relaxed_precision)
{
TOML_ANON_NAMESPACE::print_floating_point_to_stream(stream, val, format);
TOML_ANON_NAMESPACE::print_floating_point_to_stream(stream, val, format, relaxed_precision);
}
TOML_EXTERNAL_LINKAGE
void print_to_stream(std::ostream & stream, double val, value_flags format)
void print_to_stream(std::ostream & stream, double val, value_flags format, bool relaxed_precision)
{
TOML_ANON_NAMESPACE::print_floating_point_to_stream(stream, val, format);
TOML_ANON_NAMESPACE::print_floating_point_to_stream(stream, val, format, relaxed_precision);
}
TOML_EXTERNAL_LINKAGE

View File

@ -133,6 +133,12 @@ TEST_CASE("parsing - floats")
parse_expected_value(FILE_LINE_ARGS, "6.02e23"sv, 6.02e23);
parse_expected_value(FILE_LINE_ARGS, "6.02e+23"sv, 6.02e+23);
parse_expected_value(FILE_LINE_ARGS, "1.112_650_06e-17"sv, 1.11265006e-17);
parse_expected_value(FILE_LINE_ARGS, "0.010284358729827818"sv, 0.010284358729827818);
parse_expected_value(FILE_LINE_ARGS, "0.010284358729827818"sv, 0.010284358729827818);
parse_expected_value(FILE_LINE_ARGS, "0.0102"sv, 0.0102);
parse_expected_value(FILE_LINE_ARGS, "10.0102"sv, 10.0102);
parse_expected_value(FILE_LINE_ARGS, "10.010284358729828"sv, 10.010284358729828);
parse_expected_value(FILE_LINE_ARGS, "10.0"sv, 10.0);
// toml/issues/562 (hexfloats)
#if TOML_LANG_UNRELEASED

View File

@ -526,12 +526,12 @@
#endif
#endif
#if TOML_GCC || TOML_CLANG || (TOML_ICC && !TOML_ICC_CL)
#if !defined(TOML_FLOAT_CHARCONV) && (TOML_GCC || TOML_CLANG || (TOML_ICC && !TOML_ICC_CL))
// not supported by any version of GCC or Clang as of 26/11/2020
// not supported by any version of ICC on Linux as of 11/01/2021
#define TOML_FLOAT_CHARCONV 0
#endif
#if defined(__EMSCRIPTEN__) || defined(__APPLE__)
#if !defined(TOML_INT_CHARCONV) && (defined(__EMSCRIPTEN__) || defined(__APPLE__))
// causes link errors on emscripten
// causes Mac OS SDK version errors on some versions of Apple Clang
#define TOML_INT_CHARCONV 0
@ -700,9 +700,7 @@
#endif
#define TOML_MAKE_FLAGS_(name, op) \
TOML_NODISCARD \
TOML_ALWAYS_INLINE \
TOML_ATTR(const) \
TOML_CONST_INLINE_GETTER \
constexpr name operator op(name lhs, name rhs) noexcept \
{ \
using under = std::underlying_type_t<name>; \
@ -718,17 +716,13 @@
TOML_MAKE_FLAGS_(name, &); \
TOML_MAKE_FLAGS_(name, |); \
TOML_MAKE_FLAGS_(name, ^); \
TOML_NODISCARD \
TOML_ALWAYS_INLINE \
TOML_ATTR(const) \
TOML_CONST_INLINE_GETTER \
constexpr name operator~(name val) noexcept \
{ \
using under = std::underlying_type_t<name>; \
return static_cast<name>(~static_cast<under>(val)); \
} \
TOML_NODISCARD \
TOML_ALWAYS_INLINE \
TOML_ATTR(const) \
TOML_CONST_INLINE_GETTER \
constexpr bool operator!(name val) noexcept \
{ \
using under = std::underlying_type_t<name>; \
@ -1295,6 +1289,7 @@ TOML_NAMESPACE_START // abi namespace
indent_sub_tables = (1ull << 9),
indent_array_elements = (1ull << 10),
indentation = indent_sub_tables | indent_array_elements,
relaxed_float_precision = (1ull << 11),
};
TOML_MAKE_FLAGS(format_flags);
@ -2045,10 +2040,10 @@ TOML_IMPL_NAMESPACE_START
void print_to_stream(std::ostream&, uint64_t, value_flags = {}, size_t min_digits = 0);
TOML_API
void print_to_stream(std::ostream&, float, value_flags = {});
void print_to_stream(std::ostream&, float, value_flags = {}, bool relaxed_precision = false);
TOML_API
void print_to_stream(std::ostream&, double, value_flags = {});
void print_to_stream(std::ostream&, double, value_flags = {}, bool relaxed_precision = false);
TOML_API
void print_to_stream(std::ostream&, bool);
@ -9376,10 +9371,10 @@ TOML_ANON_NAMESPACE_START
inline constexpr size_t charconv_buffer_length<uint64_t> = 20; // strlen("18446744073709551615")
template <>
inline constexpr size_t charconv_buffer_length<float> = 40;
inline constexpr size_t charconv_buffer_length<float> = 64;
template <>
inline constexpr size_t charconv_buffer_length<double> = 60;
inline constexpr size_t charconv_buffer_length<double> = 64;
template <typename T>
TOML_INTERNAL_LINKAGE
@ -9470,7 +9465,10 @@ TOML_ANON_NAMESPACE_START
template <typename T>
TOML_INTERNAL_LINKAGE
void print_floating_point_to_stream(std::ostream & stream, T val, value_flags format = {})
void print_floating_point_to_stream(std::ostream & stream,
T val,
value_flags format,
[[maybe_unused]] bool relaxed_precision)
{
switch (impl::fpclassify(val))
{
@ -9492,19 +9490,30 @@ TOML_ANON_NAMESPACE_START
#if TOML_FLOAT_CHARCONV
const auto hex = !!(format & value_flags::format_as_hexadecimal);
char buf[charconv_buffer_length<T>];
const auto res = !!(format & value_flags::format_as_hexadecimal)
? std::to_chars(buf, buf + sizeof(buf), val, std::chars_format::hex)
auto res = hex ? std::to_chars(buf, buf + sizeof(buf), val, std::chars_format::hex)
: std::to_chars(buf, buf + sizeof(buf), val);
const auto str = std::string_view{ buf, static_cast<size_t>(res.ptr - buf) };
auto str = std::string_view{ buf, static_cast<size_t>(res.ptr - buf) };
char buf2[charconv_buffer_length<T>];
if (!hex && relaxed_precision)
{
res = std::to_chars(buf2, buf2 + sizeof(buf2), val, std::chars_format::general, 6);
const auto str2 = std::string_view{ buf2, static_cast<size_t>(res.ptr - buf2) };
if (str2.length() < str.length())
str = str2;
}
impl::print_to_stream(stream, str);
if (!(format & value_flags::format_as_hexadecimal) && needs_decimal_point(str))
if (!hex && needs_decimal_point(str))
toml::impl::print_to_stream(stream, ".0"sv);
#else
std::ostringstream ss;
ss.imbue(std::locale::classic());
if (!relaxed_precision)
ss.precision(std::numeric_limits<T>::max_digits10);
if (!!(format & value_flags::format_as_hexadecimal))
ss << std::hexfloat;
@ -9600,15 +9609,15 @@ TOML_IMPL_NAMESPACE_START
}
TOML_EXTERNAL_LINKAGE
void print_to_stream(std::ostream & stream, float val, value_flags format)
void print_to_stream(std::ostream & stream, float val, value_flags format, bool relaxed_precision)
{
TOML_ANON_NAMESPACE::print_floating_point_to_stream(stream, val, format);
TOML_ANON_NAMESPACE::print_floating_point_to_stream(stream, val, format, relaxed_precision);
}
TOML_EXTERNAL_LINKAGE
void print_to_stream(std::ostream & stream, double val, value_flags format)
void print_to_stream(std::ostream & stream, double val, value_flags format, bool relaxed_precision)
{
TOML_ANON_NAMESPACE::print_floating_point_to_stream(stream, val, format);
TOML_ANON_NAMESPACE::print_floating_point_to_stream(stream, val, format, relaxed_precision);
}
TOML_EXTERNAL_LINKAGE
@ -15244,7 +15253,12 @@ TOML_IMPL_NAMESPACE_START
case fp_class::neg_inf: inf_nan = &constants_->float_neg_inf; break;
case fp_class::pos_inf: inf_nan = &constants_->float_pos_inf; break;
case fp_class::nan: inf_nan = &constants_->float_nan; break;
case fp_class::ok: print_to_stream(*stream_, *val); break;
case fp_class::ok:
print_to_stream(*stream_,
*val,
value_flags::none,
!!(config_.flags & format_flags::relaxed_float_precision));
break;
default: TOML_UNREACHABLE;
}