🔨 cleanup after #915

This commit is contained in:
Niels Lohmann 2018-01-21 15:55:35 +01:00
parent 010e596001
commit 3cca630836
No known key found for this signature in database
GPG key ID: 7F3CEA63AE251B69
6 changed files with 521 additions and 385 deletions

View file

@ -8,6 +8,7 @@ SRCS = develop/json.hpp \
develop/detail/exceptions.hpp \ develop/detail/exceptions.hpp \
develop/detail/value_t.hpp \ develop/detail/value_t.hpp \
develop/detail/conversions/from_json.hpp \ develop/detail/conversions/from_json.hpp \
develop/detail/conversions/to_chars.hpp \
develop/detail/conversions/to_json.hpp \ develop/detail/conversions/to_json.hpp \
develop/detail/parsing/input_adapters.hpp \ develop/detail/parsing/input_adapters.hpp \
develop/detail/parsing/lexer.hpp \ develop/detail/parsing/lexer.hpp \

View file

@ -821,8 +821,6 @@ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR I
The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed under the [MIT License](http://opensource.org/licenses/MIT) (see above). Copyright &copy; 2008-2009 [Björn Hoehrmann](http://bjoern.hoehrmann.de/) <bjoern@hoehrmann.de> The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed under the [MIT License](http://opensource.org/licenses/MIT) (see above). Copyright &copy; 2008-2009 [Björn Hoehrmann](http://bjoern.hoehrmann.de/) <bjoern@hoehrmann.de>
* * *
The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](http://opensource.org/licenses/MIT) (see above). Copyright &copy; 2009 [Florian Loitsch](http://florian.loitsch.com/) The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](http://opensource.org/licenses/MIT) (see above). Copyright &copy; 2009 [Florian Loitsch](http://florian.loitsch.com/)
## Contact ## Contact
@ -934,6 +932,7 @@ I deeply appreciate the help of the following people.
- [Matthias Möller](https://github.com/TinyTinni) added a `.natvis` for the MSVC debug view. - [Matthias Möller](https://github.com/TinyTinni) added a `.natvis` for the MSVC debug view.
- [bogemic](https://github.com/bogemic) fixed some C++17 deprecation warnings. - [bogemic](https://github.com/bogemic) fixed some C++17 deprecation warnings.
- [Eren Okka](https://github.com/erengy) fixed some MSVC warnings. - [Eren Okka](https://github.com/erengy) fixed some MSVC warnings.
- [abolz](https://github.com/abolz) integrated the Grisu2 algorithm for proper floating-point formatting, allowing more roundtrip checks to succeed.
Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone. Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone.

View file

@ -11,24 +11,30 @@ namespace nlohmann
namespace detail namespace detail
{ {
// Implements the Grisu2 algorithm for binary to decimal floating-point conversion. /*!
// @brief implements the Grisu2 algorithm for binary to decimal floating-point
// This implementation is a slightly modified version of the reference implementation which may be conversion.
// obtained from http://florian.loitsch.com/publications (bench.tar.gz).
// This implementation is a slightly modified version of the reference
// The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch. implementation which may be obtained from
// http://florian.loitsch.com/publications (bench.tar.gz).
// For a detailed description of the algorithm see:
// The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch.
// [1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with Integers",
// Proceedings of the ACM SIGPLAN 2010 Conference on Programming Language Design and Implementation, PLDI 2010 For a detailed description of the algorithm see:
// [2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately",
// Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language Design and Implementation, PLDI 1996 [1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with
Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming
Language Design and Implementation, PLDI 2010
[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately",
Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language
Design and Implementation, PLDI 1996
*/
namespace dtoa_impl namespace dtoa_impl
{ {
template <typename Target, typename Source> template <typename Target, typename Source>
Target reinterpret_bits(Source source) Target reinterpret_bits(const Source source)
{ {
static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); static_assert(sizeof(Target) == sizeof(Source), "size mismatch");
@ -44,36 +50,27 @@ struct diyfp // f * 2^e
uint64_t f; uint64_t f;
int e; int e;
constexpr diyfp() : f(0), e(0) {} constexpr diyfp() noexcept : f(0), e(0) {}
constexpr diyfp(uint64_t f_, int e_) : f(f_), e(e_) {} constexpr diyfp(uint64_t f_, int e_) noexcept : f(f_), e(e_) {}
// Returns x - y. /*!
// PRE: x.e == y.e and x.f >= y.f @brief returns x - y
static diyfp sub(diyfp x, diyfp y); @pre x.e == y.e and x.f >= y.f
*/
// Returns x * y. static diyfp sub(const diyfp& x, const diyfp& y) noexcept
// The result is rounded. (Only the upper q bits are returned.) {
static diyfp mul(diyfp x, diyfp y);
// Normalize x such that the significand is >= 2^(q-1).
// PRE: x.f != 0
static diyfp normalize(diyfp x);
// Normalize x such that the result has the exponent E.
// PRE: e >= x.e and the upper e - x.e bits of x.f must be zero.
static diyfp normalize_to(diyfp x, int e);
};
inline diyfp diyfp::sub(diyfp x, diyfp y)
{
assert(x.e == y.e); assert(x.e == y.e);
assert(x.f >= y.f); assert(x.f >= y.f);
return diyfp(x.f - y.f, x.e); return diyfp(x.f - y.f, x.e);
} }
inline diyfp diyfp::mul(diyfp x, diyfp y) /*!
{ @brief returns x * y
@note The result is rounded. (Only the upper q bits are returned.)
*/
static diyfp mul(const diyfp& x, const diyfp& y) noexcept
{
static_assert(kPrecision == 64, "internal error"); static_assert(kPrecision == 64, "internal error");
// Computes: // Computes:
@ -131,10 +128,14 @@ inline diyfp diyfp::mul(diyfp x, diyfp y)
const uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32); const uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32);
return diyfp(h, x.e + y.e + 64); return diyfp(h, x.e + y.e + 64);
} }
inline diyfp diyfp::normalize(diyfp x) /*!
{ @brief normalize x such that the significand is >= 2^(q-1)
@pre x.f != 0
*/
static diyfp normalize(diyfp x) noexcept
{
assert(x.f != 0); assert(x.f != 0);
while ((x.f >> 63) == 0) while ((x.f >> 63) == 0)
@ -144,17 +145,22 @@ inline diyfp diyfp::normalize(diyfp x)
} }
return x; return x;
} }
inline diyfp diyfp::normalize_to(diyfp x, int target_exponent) /*!
{ @brief normalize x such that the result has the exponent E
@pre e >= x.e and the upper e - x.e bits of x.f must be zero.
*/
static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept
{
const int delta = x.e - target_exponent; const int delta = x.e - target_exponent;
assert(delta >= 0); assert(delta >= 0);
assert(((x.f << delta) >> delta) == x.f); assert(((x.f << delta) >> delta) == x.f);
return diyfp(x.f << delta, target_exponent); return diyfp(x.f << delta, target_exponent);
} }
};
struct boundaries struct boundaries
{ {
@ -163,8 +169,12 @@ struct boundaries
diyfp plus; diyfp plus;
}; };
// Compute the (normalized) diyfp representing the input number 'value' and its boundaries. /*!
// PRE: value must be finite and positive Compute the (normalized) diyfp representing the input number 'value' and its
boundaries.
@pre value must be finite and positive
*/
template <typename FloatType> template <typename FloatType>
boundaries compute_boundaries(FloatType value) boundaries compute_boundaries(FloatType value)
{ {
@ -193,9 +203,7 @@ boundaries compute_boundaries(FloatType value)
const uint64_t F = bits & (kHiddenBit - 1); const uint64_t F = bits & (kHiddenBit - 1);
const bool is_denormal = (E == 0); const bool is_denormal = (E == 0);
const diyfp v = is_denormal
const diyfp v
= is_denormal
? diyfp(F, 1 - kBias) ? diyfp(F, 1 - kBias)
: diyfp(F + kHiddenBit, static_cast<int>(E) - kBias); : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias);
@ -221,10 +229,8 @@ boundaries compute_boundaries(FloatType value)
// v- m- v m+ v+ // v- m- v m+ v+
const bool lower_boundary_is_closer = (F == 0 and E > 1); const bool lower_boundary_is_closer = (F == 0 and E > 1);
const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1);
const diyfp m_minus const diyfp m_minus = lower_boundary_is_closer
= lower_boundary_is_closer
? diyfp(4 * v.f - 1, v.e - 2) // (B) ? diyfp(4 * v.f - 1, v.e - 2) // (B)
: diyfp(2 * v.f - 1, v.e - 1); // (A) : diyfp(2 * v.f - 1, v.e - 1); // (A)
@ -302,12 +308,13 @@ struct cached_power // c = f * 2^e ~= 10^k
int k; int k;
}; };
// For a normalized diyfp w = f * 2^e, this function returns a (normalized) /*!
// cached power-of-ten c = f_c * 2^e_c, such that the exponent of the product For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached
// w * c satisfies (Definition 3.2 from [1]) power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c
// satisfies (Definition 3.2 from [1])
// alpha <= e_c + e + q <= gamma.
// alpha <= e_c + e + q <= gamma.
*/
inline cached_power get_cached_power_for_binary_exponent(int e) inline cached_power get_cached_power_for_binary_exponent(int e)
{ {
// Now // Now
@ -468,24 +475,66 @@ inline cached_power get_cached_power_for_binary_exponent(int e)
return cached; return cached;
} }
// For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. /*!
// For n == 0, returns 1 and sets pow10 := 1. For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k.
inline int find_largest_pow10(uint32_t n, uint32_t& pow10) For n == 0, returns 1 and sets pow10 := 1.
*/
inline int find_largest_pow10(const uint32_t n, uint32_t& pow10)
{ {
if (n >= 1000000000) { pow10 = 1000000000; return 10; } if (n >= 1000000000)
if (n >= 100000000) { pow10 = 100000000; return 9; } {
if (n >= 10000000) { pow10 = 10000000; return 8; } pow10 = 1000000000;
if (n >= 1000000) { pow10 = 1000000; return 7; } return 10;
if (n >= 100000) { pow10 = 100000; return 6; } }
if (n >= 10000) { pow10 = 10000; return 5; } else if (n >= 100000000)
if (n >= 1000) { pow10 = 1000; return 4; } {
if (n >= 100) { pow10 = 100; return 3; } pow10 = 100000000;
if (n >= 10) { pow10 = 10; return 2; } return 9;
}
pow10 = 1; return 1; else if (n >= 10000000)
{
pow10 = 10000000;
return 8;
}
else if (n >= 1000000)
{
pow10 = 1000000;
return 7;
}
else if (n >= 100000)
{
pow10 = 100000;
return 6;
}
else if (n >= 10000)
{
pow10 = 10000;
return 5;
}
else if (n >= 1000)
{
pow10 = 1000;
return 4;
}
else if (n >= 100)
{
pow10 = 100;
return 3;
}
else if (n >= 10)
{
pow10 = 10;
return 2;
}
else
{
pow10 = 1;
return 1;
}
} }
inline void grisu2_round(char* buf, int len, uint64_t dist, uint64_t delta, uint64_t rest, uint64_t ten_k) inline void grisu2_round(char* buf, int len, uint64_t dist, uint64_t delta,
uint64_t rest, uint64_t ten_k)
{ {
assert(len >= 1); assert(len >= 1);
assert(dist <= delta); assert(dist <= delta);
@ -521,9 +570,12 @@ inline void grisu2_round(char* buf, int len, uint64_t dist, uint64_t delta, uint
} }
} }
// Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. /*!
// M- and M+ must be normalized and share the same exponent -60 <= e <= -32. Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+.
inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, diyfp M_minus, diyfp w, diyfp M_plus) M- and M+ must be normalized and share the same exponent -60 <= e <= -32.
*/
inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
diyfp M_minus, diyfp w, diyfp M_plus)
{ {
static_assert(kAlpha >= -60, "internal error"); static_assert(kAlpha >= -60, "internal error");
static_assert(kGamma <= -32, "internal error"); static_assert(kGamma <= -32, "internal error");
@ -757,10 +809,13 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, d
// N = 9 for p = 24 (IEEE single precision) // N = 9 for p = 24 (IEEE single precision)
} }
// v = buf * 10^decimal_exponent /*!
// len is the length of the buffer (number of decimal digits) v = buf * 10^decimal_exponent
// The buffer must be large enough, i.e. >= max_digits10. len is the length of the buffer (number of decimal digits)
inline void grisu2(char* buf, int& len, int& decimal_exponent, diyfp m_minus, diyfp v, diyfp m_plus) The buffer must be large enough, i.e. >= max_digits10.
*/
inline void grisu2(char* buf, int& len, int& decimal_exponent,
diyfp m_minus, diyfp v, diyfp m_plus)
{ {
assert(m_plus.e == m_minus.e); assert(m_plus.e == m_minus.e);
assert(m_plus.e == v.e); assert(m_plus.e == v.e);
@ -812,9 +867,11 @@ inline void grisu2(char* buf, int& len, int& decimal_exponent, diyfp m_minus, di
grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus);
} }
// v = buf * 10^decimal_exponent /*!
// len is the length of the buffer (number of decimal digits) v = buf * 10^decimal_exponent
// The buffer must be large enough, i.e. >= max_digits10. len is the length of the buffer (number of decimal digits)
The buffer must be large enough, i.e. >= max_digits10.
*/
template <typename FloatType> template <typename FloatType>
void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)
{ {
@ -849,9 +906,11 @@ void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)
grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);
} }
// Appends a decimal representation of e to buf. /*!
// Returns a pointer to the element following the exponent. @brief appends a decimal representation of e to buf
// PRE: -1000 < e < 1000 @return a pointer to the element following the exponent.
@pre -1000 < e < 1000
*/
inline char* append_exponent(char* buf, int e) inline char* append_exponent(char* buf, int e)
{ {
assert(e > -1000); assert(e > -1000);
@ -893,12 +952,17 @@ inline char* append_exponent(char* buf, int e)
return buf; return buf;
} }
// Prettify v = buf * 10^decimal_exponent /*!
// If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point notation. @brief prettify v = buf * 10^decimal_exponent
// Otherwise it will be printed in exponential notation.
// PRE: min_exp < 0 If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point
// PRE: max_exp > 0 notation. Otherwise it will be printed in exponential notation.
inline char* format_buffer(char* buf, int len, int decimal_exponent, int min_exp, int max_exp)
@pre min_exp < 0
@pre max_exp > 0
*/
inline char* format_buffer(char* buf, int len, int decimal_exponent,
int min_exp, int max_exp)
{ {
assert(min_exp < 0); assert(min_exp < 0);
assert(max_exp > 0); assert(max_exp > 0);
@ -969,14 +1033,16 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent, int min_exp
} // namespace dtoa_impl } // namespace dtoa_impl
// Generates a decimal representation of the floating-point number value in [first, last). /*!
// @brief generates a decimal representation of the floating-point number value in [first, last).
// The format of the resulting decimal representation is similar to printf's %g format.
// Returns an iterator pointing past-the-end of the decimal representation. The format of the resulting decimal representation is similar to printf's %g
// format. Returns an iterator pointing past-the-end of the decimal representation.
// Note: The input number must be finite, i.e. NaN's and Inf's are not supported.
// Note: The buffer must be large enough. @note The input number must be finite, i.e. NaN's and Inf's are not supported.
// Note: The result is NOT null-terminated. @note The buffer must be large enough.
@note The result is NOT null-terminated.
*/
template <typename FloatType> template <typename FloatType>
char* to_chars(char* first, char* last, FloatType value) char* to_chars(char* first, char* last, FloatType value)
{ {

View file

@ -483,8 +483,8 @@ class serializer
} }
// If number_float_t is an IEEE-754 single or double precision number, // If number_float_t is an IEEE-754 single or double precision number,
// use the Grisu2 algorithm to produce short numbers which are guaranteed // use the Grisu2 algorithm to produce short numbers which are
// to round-trip, using strtof and strtod, resp. // guaranteed to round-trip, using strtof and strtod, resp.
// //
// NB: The test below works if <long double> == <double>. // NB: The test below works if <long double> == <double>.
static constexpr bool is_ieee_single_or_double static constexpr bool is_ieee_single_or_double

View file

@ -7077,24 +7077,30 @@ namespace nlohmann
namespace detail namespace detail
{ {
// Implements the Grisu2 algorithm for binary to decimal floating-point conversion. /*!
// @brief implements the Grisu2 algorithm for binary to decimal floating-point
// This implementation is a slightly modified version of the reference implementation which may be conversion.
// obtained from http://florian.loitsch.com/publications (bench.tar.gz).
// This implementation is a slightly modified version of the reference
// The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch. implementation which may be obtained from
// http://florian.loitsch.com/publications (bench.tar.gz).
// For a detailed description of the algorithm see:
// The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch.
// [1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with Integers",
// Proceedings of the ACM SIGPLAN 2010 Conference on Programming Language Design and Implementation, PLDI 2010 For a detailed description of the algorithm see:
// [2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately",
// Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language Design and Implementation, PLDI 1996 [1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with
Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming
Language Design and Implementation, PLDI 2010
[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately",
Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language
Design and Implementation, PLDI 1996
*/
namespace dtoa_impl namespace dtoa_impl
{ {
template <typename Target, typename Source> template <typename Target, typename Source>
Target reinterpret_bits(Source source) Target reinterpret_bits(const Source source)
{ {
static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); static_assert(sizeof(Target) == sizeof(Source), "size mismatch");
@ -7110,36 +7116,27 @@ struct diyfp // f * 2^e
uint64_t f; uint64_t f;
int e; int e;
constexpr diyfp() : f(0), e(0) {} constexpr diyfp() noexcept : f(0), e(0) {}
constexpr diyfp(uint64_t f_, int e_) : f(f_), e(e_) {} constexpr diyfp(uint64_t f_, int e_) noexcept : f(f_), e(e_) {}
// Returns x - y. /*!
// PRE: x.e == y.e and x.f >= y.f @brief returns x - y
static diyfp sub(diyfp x, diyfp y); @pre x.e == y.e and x.f >= y.f
*/
// Returns x * y. static diyfp sub(const diyfp& x, const diyfp& y) noexcept
// The result is rounded. (Only the upper q bits are returned.) {
static diyfp mul(diyfp x, diyfp y);
// Normalize x such that the significand is >= 2^(q-1).
// PRE: x.f != 0
static diyfp normalize(diyfp x);
// Normalize x such that the result has the exponent E.
// PRE: e >= x.e and the upper e - x.e bits of x.f must be zero.
static diyfp normalize_to(diyfp x, int e);
};
inline diyfp diyfp::sub(diyfp x, diyfp y)
{
assert(x.e == y.e); assert(x.e == y.e);
assert(x.f >= y.f); assert(x.f >= y.f);
return diyfp(x.f - y.f, x.e); return diyfp(x.f - y.f, x.e);
} }
inline diyfp diyfp::mul(diyfp x, diyfp y) /*!
{ @brief returns x * y
@note The result is rounded. (Only the upper q bits are returned.)
*/
static diyfp mul(const diyfp& x, const diyfp& y) noexcept
{
static_assert(kPrecision == 64, "internal error"); static_assert(kPrecision == 64, "internal error");
// Computes: // Computes:
@ -7197,10 +7194,14 @@ inline diyfp diyfp::mul(diyfp x, diyfp y)
const uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32); const uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32);
return diyfp(h, x.e + y.e + 64); return diyfp(h, x.e + y.e + 64);
} }
inline diyfp diyfp::normalize(diyfp x) /*!
{ @brief normalize x such that the significand is >= 2^(q-1)
@pre x.f != 0
*/
static diyfp normalize(diyfp x) noexcept
{
assert(x.f != 0); assert(x.f != 0);
while ((x.f >> 63) == 0) while ((x.f >> 63) == 0)
@ -7210,17 +7211,22 @@ inline diyfp diyfp::normalize(diyfp x)
} }
return x; return x;
} }
inline diyfp diyfp::normalize_to(diyfp x, int target_exponent) /*!
{ @brief normalize x such that the result has the exponent E
@pre e >= x.e and the upper e - x.e bits of x.f must be zero.
*/
static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept
{
const int delta = x.e - target_exponent; const int delta = x.e - target_exponent;
assert(delta >= 0); assert(delta >= 0);
assert(((x.f << delta) >> delta) == x.f); assert(((x.f << delta) >> delta) == x.f);
return diyfp(x.f << delta, target_exponent); return diyfp(x.f << delta, target_exponent);
} }
};
struct boundaries struct boundaries
{ {
@ -7229,8 +7235,12 @@ struct boundaries
diyfp plus; diyfp plus;
}; };
// Compute the (normalized) diyfp representing the input number 'value' and its boundaries. /*!
// PRE: value must be finite and positive Compute the (normalized) diyfp representing the input number 'value' and its
boundaries.
@pre value must be finite and positive
*/
template <typename FloatType> template <typename FloatType>
boundaries compute_boundaries(FloatType value) boundaries compute_boundaries(FloatType value)
{ {
@ -7259,9 +7269,7 @@ boundaries compute_boundaries(FloatType value)
const uint64_t F = bits & (kHiddenBit - 1); const uint64_t F = bits & (kHiddenBit - 1);
const bool is_denormal = (E == 0); const bool is_denormal = (E == 0);
const diyfp v = is_denormal
const diyfp v
= is_denormal
? diyfp(F, 1 - kBias) ? diyfp(F, 1 - kBias)
: diyfp(F + kHiddenBit, static_cast<int>(E) - kBias); : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias);
@ -7287,10 +7295,8 @@ boundaries compute_boundaries(FloatType value)
// v- m- v m+ v+ // v- m- v m+ v+
const bool lower_boundary_is_closer = (F == 0 and E > 1); const bool lower_boundary_is_closer = (F == 0 and E > 1);
const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1);
const diyfp m_minus const diyfp m_minus = lower_boundary_is_closer
= lower_boundary_is_closer
? diyfp(4 * v.f - 1, v.e - 2) // (B) ? diyfp(4 * v.f - 1, v.e - 2) // (B)
: diyfp(2 * v.f - 1, v.e - 1); // (A) : diyfp(2 * v.f - 1, v.e - 1); // (A)
@ -7368,12 +7374,13 @@ struct cached_power // c = f * 2^e ~= 10^k
int k; int k;
}; };
// For a normalized diyfp w = f * 2^e, this function returns a (normalized) /*!
// cached power-of-ten c = f_c * 2^e_c, such that the exponent of the product For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached
// w * c satisfies (Definition 3.2 from [1]) power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c
// satisfies (Definition 3.2 from [1])
// alpha <= e_c + e + q <= gamma.
// alpha <= e_c + e + q <= gamma.
*/
inline cached_power get_cached_power_for_binary_exponent(int e) inline cached_power get_cached_power_for_binary_exponent(int e)
{ {
// Now // Now
@ -7534,24 +7541,66 @@ inline cached_power get_cached_power_for_binary_exponent(int e)
return cached; return cached;
} }
// For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. /*!
// For n == 0, returns 1 and sets pow10 := 1. For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k.
inline int find_largest_pow10(uint32_t n, uint32_t& pow10) For n == 0, returns 1 and sets pow10 := 1.
*/
inline int find_largest_pow10(const uint32_t n, uint32_t& pow10)
{ {
if (n >= 1000000000) { pow10 = 1000000000; return 10; } if (n >= 1000000000)
if (n >= 100000000) { pow10 = 100000000; return 9; } {
if (n >= 10000000) { pow10 = 10000000; return 8; } pow10 = 1000000000;
if (n >= 1000000) { pow10 = 1000000; return 7; } return 10;
if (n >= 100000) { pow10 = 100000; return 6; } }
if (n >= 10000) { pow10 = 10000; return 5; } else if (n >= 100000000)
if (n >= 1000) { pow10 = 1000; return 4; } {
if (n >= 100) { pow10 = 100; return 3; } pow10 = 100000000;
if (n >= 10) { pow10 = 10; return 2; } return 9;
}
pow10 = 1; return 1; else if (n >= 10000000)
{
pow10 = 10000000;
return 8;
}
else if (n >= 1000000)
{
pow10 = 1000000;
return 7;
}
else if (n >= 100000)
{
pow10 = 100000;
return 6;
}
else if (n >= 10000)
{
pow10 = 10000;
return 5;
}
else if (n >= 1000)
{
pow10 = 1000;
return 4;
}
else if (n >= 100)
{
pow10 = 100;
return 3;
}
else if (n >= 10)
{
pow10 = 10;
return 2;
}
else
{
pow10 = 1;
return 1;
}
} }
inline void grisu2_round(char* buf, int len, uint64_t dist, uint64_t delta, uint64_t rest, uint64_t ten_k) inline void grisu2_round(char* buf, int len, uint64_t dist, uint64_t delta,
uint64_t rest, uint64_t ten_k)
{ {
assert(len >= 1); assert(len >= 1);
assert(dist <= delta); assert(dist <= delta);
@ -7587,9 +7636,12 @@ inline void grisu2_round(char* buf, int len, uint64_t dist, uint64_t delta, uint
} }
} }
// Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. /*!
// M- and M+ must be normalized and share the same exponent -60 <= e <= -32. Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+.
inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, diyfp M_minus, diyfp w, diyfp M_plus) M- and M+ must be normalized and share the same exponent -60 <= e <= -32.
*/
inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
diyfp M_minus, diyfp w, diyfp M_plus)
{ {
static_assert(kAlpha >= -60, "internal error"); static_assert(kAlpha >= -60, "internal error");
static_assert(kGamma <= -32, "internal error"); static_assert(kGamma <= -32, "internal error");
@ -7823,10 +7875,13 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, d
// N = 9 for p = 24 (IEEE single precision) // N = 9 for p = 24 (IEEE single precision)
} }
// v = buf * 10^decimal_exponent /*!
// len is the length of the buffer (number of decimal digits) v = buf * 10^decimal_exponent
// The buffer must be large enough, i.e. >= max_digits10. len is the length of the buffer (number of decimal digits)
inline void grisu2(char* buf, int& len, int& decimal_exponent, diyfp m_minus, diyfp v, diyfp m_plus) The buffer must be large enough, i.e. >= max_digits10.
*/
inline void grisu2(char* buf, int& len, int& decimal_exponent,
diyfp m_minus, diyfp v, diyfp m_plus)
{ {
assert(m_plus.e == m_minus.e); assert(m_plus.e == m_minus.e);
assert(m_plus.e == v.e); assert(m_plus.e == v.e);
@ -7878,9 +7933,11 @@ inline void grisu2(char* buf, int& len, int& decimal_exponent, diyfp m_minus, di
grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus);
} }
// v = buf * 10^decimal_exponent /*!
// len is the length of the buffer (number of decimal digits) v = buf * 10^decimal_exponent
// The buffer must be large enough, i.e. >= max_digits10. len is the length of the buffer (number of decimal digits)
The buffer must be large enough, i.e. >= max_digits10.
*/
template <typename FloatType> template <typename FloatType>
void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)
{ {
@ -7915,9 +7972,11 @@ void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)
grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);
} }
// Appends a decimal representation of e to buf. /*!
// Returns a pointer to the element following the exponent. @brief appends a decimal representation of e to buf
// PRE: -1000 < e < 1000 @return a pointer to the element following the exponent.
@pre -1000 < e < 1000
*/
inline char* append_exponent(char* buf, int e) inline char* append_exponent(char* buf, int e)
{ {
assert(e > -1000); assert(e > -1000);
@ -7959,12 +8018,17 @@ inline char* append_exponent(char* buf, int e)
return buf; return buf;
} }
// Prettify v = buf * 10^decimal_exponent /*!
// If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point notation. @brief prettify v = buf * 10^decimal_exponent
// Otherwise it will be printed in exponential notation.
// PRE: min_exp < 0 If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point
// PRE: max_exp > 0 notation. Otherwise it will be printed in exponential notation.
inline char* format_buffer(char* buf, int len, int decimal_exponent, int min_exp, int max_exp)
@pre min_exp < 0
@pre max_exp > 0
*/
inline char* format_buffer(char* buf, int len, int decimal_exponent,
int min_exp, int max_exp)
{ {
assert(min_exp < 0); assert(min_exp < 0);
assert(max_exp > 0); assert(max_exp > 0);
@ -8035,14 +8099,16 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent, int min_exp
} // namespace dtoa_impl } // namespace dtoa_impl
// Generates a decimal representation of the floating-point number value in [first, last). /*!
// @brief generates a decimal representation of the floating-point number value in [first, last).
// The format of the resulting decimal representation is similar to printf's %g format.
// Returns an iterator pointing past-the-end of the decimal representation. The format of the resulting decimal representation is similar to printf's %g
// format. Returns an iterator pointing past-the-end of the decimal representation.
// Note: The input number must be finite, i.e. NaN's and Inf's are not supported.
// Note: The buffer must be large enough. @note The input number must be finite, i.e. NaN's and Inf's are not supported.
// Note: The result is NOT null-terminated. @note The buffer must be large enough.
@note The result is NOT null-terminated.
*/
template <typename FloatType> template <typename FloatType>
char* to_chars(char* first, char* last, FloatType value) char* to_chars(char* first, char* last, FloatType value)
{ {
@ -8564,8 +8630,8 @@ class serializer
} }
// If number_float_t is an IEEE-754 single or double precision number, // If number_float_t is an IEEE-754 single or double precision number,
// use the Grisu2 algorithm to produce short numbers which are guaranteed // use the Grisu2 algorithm to produce short numbers which are
// to round-trip, using strtof and strtod, resp. // guaranteed to round-trip, using strtof and strtod, resp.
// //
// NB: The test below works if <long double> == <double>. // NB: The test below works if <long double> == <double>.
static constexpr bool is_ieee_single_or_double static constexpr bool is_ieee_single_or_double

View file

@ -60,26 +60,28 @@ static float make_float(uint64_t f, int e)
constexpr int kDenormalExponent = 1 - kExponentBias; constexpr int kDenormalExponent = 1 - kExponentBias;
constexpr int kMaxExponent = 0xFF - kExponentBias; constexpr int kMaxExponent = 0xFF - kExponentBias;
while (f > kHiddenBit + kSignificandMask) { while (f > kHiddenBit + kSignificandMask)
{
f >>= 1; f >>= 1;
e++; e++;
} }
if (e >= kMaxExponent) { if (e >= kMaxExponent)
{
return std::numeric_limits<float>::infinity(); return std::numeric_limits<float>::infinity();
} }
if (e < kDenormalExponent) { if (e < kDenormalExponent)
{
return 0.0; return 0.0;
} }
while (e > kDenormalExponent && (f & kHiddenBit) == 0) { while (e > kDenormalExponent && (f & kHiddenBit) == 0)
{
f <<= 1; f <<= 1;
e--; e--;
} }
uint64_t biased_exponent; uint64_t biased_exponent = (e == kDenormalExponent && (f & kHiddenBit) == 0)
if (e == kDenormalExponent && (f & kHiddenBit) == 0) ? 0
biased_exponent = 0; : static_cast<uint64_t>(e + kExponentBias);
else
biased_exponent = static_cast<uint64_t>(e + kExponentBias);
uint64_t bits = (f & kSignificandMask) | (biased_exponent << kPhysicalSignificandSize); uint64_t bits = (f & kSignificandMask) | (biased_exponent << kPhysicalSignificandSize);
return reinterpret_bits<float>(static_cast<uint32_t>(bits)); return reinterpret_bits<float>(static_cast<uint32_t>(bits));
@ -110,26 +112,28 @@ static double make_double(uint64_t f, int e)
constexpr int kDenormalExponent = 1 - kExponentBias; constexpr int kDenormalExponent = 1 - kExponentBias;
constexpr int kMaxExponent = 0x7FF - kExponentBias; constexpr int kMaxExponent = 0x7FF - kExponentBias;
while (f > kHiddenBit + kSignificandMask) { while (f > kHiddenBit + kSignificandMask)
{
f >>= 1; f >>= 1;
e++; e++;
} }
if (e >= kMaxExponent) { if (e >= kMaxExponent)
{
return std::numeric_limits<double>::infinity(); return std::numeric_limits<double>::infinity();
} }
if (e < kDenormalExponent) { if (e < kDenormalExponent)
{
return 0.0; return 0.0;
} }
while (e > kDenormalExponent && (f & kHiddenBit) == 0) { while (e > kDenormalExponent && (f & kHiddenBit) == 0)
{
f <<= 1; f <<= 1;
e--; e--;
} }
uint64_t biased_exponent; uint64_t biased_exponent = (e == kDenormalExponent && (f & kHiddenBit) == 0)
if (e == kDenormalExponent && (f & kHiddenBit) == 0) ? 0
biased_exponent = 0; : static_cast<uint64_t>(e + kExponentBias);
else
biased_exponent = static_cast<uint64_t>(e + kExponentBias);
uint64_t bits = (f & kSignificandMask) | (biased_exponent << kPhysicalSignificandSize); uint64_t bits = (f & kSignificandMask) | (biased_exponent << kPhysicalSignificandSize);
return reinterpret_bits<double>(bits); return reinterpret_bits<double>(bits);
@ -139,7 +143,7 @@ TEST_CASE("digit gen")
{ {
SECTION("single precision") SECTION("single precision")
{ {
auto check_float = [](float number, const std::string& digits, int expected_exponent) auto check_float = [](float number, const std::string & digits, int expected_exponent)
{ {
CAPTURE(number); CAPTURE(number);
CAPTURE(digits); CAPTURE(digits);
@ -203,7 +207,7 @@ TEST_CASE("digit gen")
SECTION("double precision") SECTION("double precision")
{ {
auto check_double = [](double number, const std::string& digits, int expected_exponent) auto check_double = [](double number, const std::string & digits, int expected_exponent)
{ {
CAPTURE(number); CAPTURE(number);
CAPTURE(digits); CAPTURE(digits);
@ -349,7 +353,7 @@ TEST_CASE("formatting")
{ {
SECTION("single precision") SECTION("single precision")
{ {
auto check_float = [](float number, const std::string& expected) auto check_float = [](float number, const std::string & expected)
{ {
char buf[32]; char buf[32];
char* end = nlohmann::detail::to_chars(buf, buf + 32, number); char* end = nlohmann::detail::to_chars(buf, buf + 32, number);
@ -409,7 +413,7 @@ TEST_CASE("formatting")
SECTION("double precision") SECTION("double precision")
{ {
auto check_double = [](double number, const std::string& expected) auto check_double = [](double number, const std::string & expected)
{ {
char buf[32]; char buf[32];
char* end = nlohmann::detail::to_chars(buf, buf + 32, number); char* end = nlohmann::detail::to_chars(buf, buf + 32, number);