From 22f3f88771a7a531e6105d4993a427787090fa37 Mon Sep 17 00:00:00 2001 From: Jett Date: Sun, 16 Oct 2016 07:07:28 -0500 Subject: [PATCH 01/13] new strtod/ld/f function --- src/json.hpp | 201 +++++++++++++++++++++++++++++++++++++--------- src/json.hpp.re2c | 201 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 324 insertions(+), 78 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 9c0df9d497..59b69294cc 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -8800,64 +8800,187 @@ class basic_json return result; } + // non locale aware isspace + bool nl_isspace(const char c) const + { + return + c == 0x20 or + c == 0x09 or + c == 0x0a or + c == 0x0b or + c == 0x0c or + c == 0x0d; + } + + // non locale aware isdigit + // Microsoft in 1252 codepage and others may classify additional single-byte characters as digits using std::isdigit + bool nl_isdigit(const char c) const + { + return c >= '0' and c <= '9'; + } + /*! - @brief parse floating point number + @brief parse string to floating point number + + This function is a reimplementation of the strtold family without + regard to locale - This function (and its overloads) serves to select the most approprate - standard floating point number parsing function based on the type - supplied via the first parameter. Set this to @a - static_cast(nullptr). + @tparam T a is_floating_point type - @param[in] type the @ref number_float_t in use + @param[in] st the string we will parse @param[in,out] endptr recieves a pointer to the first character after the number @return the floating point number */ - long double str_to_float_t(long double* /* type */, char** endptr) const + template ::value>::type> + T strtox(const char *st, char **endptr) const { - return std::strtold(reinterpret_cast(m_start), endptr); - } + constexpr std::array powerof10 { + {1.e1L, 1.e2L, 1.e4L, 1.e8L, 1.e16L, 1.e32L, 1.e64L, 1.e128L, 1.e256L} + }; - /*! - @brief parse floating point number + T result = 0; + const char *fst = st; + bool successful_parse = false; - This function (and its overloads) serves to select the most approprate - standard floating point number parsing function based on the type - supplied via the first parameter. Set this to @a - static_cast(nullptr). + while (nl_isspace(*fst)) + { + ++fst; + } - @param[in] type the @ref number_float_t in use + char cp = *fst; + int exp = 0; // exponent + { + const bool negative_sign = cp == '-'; - @param[in,out] endptr recieves a pointer to the first character after - the number + if (cp == '-' or cp == '+') + { + ++fst; + successful_parse = true; + } - @return the floating point number - */ - double str_to_float_t(double* /* type */, char** endptr) const - { - return std::strtod(reinterpret_cast(m_start), endptr); - } + // read in fractional part of number, until an 'e' is reached. + // count digits after decimal point. + while (nl_isdigit(cp = *fst)) + { + result = result * 10 + (cp - '0'); + successful_parse = true; + ++fst; + } - /*! - @brief parse floating point number + if (cp == '.') + { + while (nl_isdigit(cp = *++fst)) + { + result = result * 10 + (cp - '0'); + successful_parse = true; + --exp; + } + } - This function (and its overloads) serves to select the most approprate - standard floating point number parsing function based on the type - supplied via the first parameter. Set this to @a - static_cast(nullptr). + // if negative number, reverse sign + if (negative_sign) + { + result = -result; + } + } - @param[in] type the @ref number_float_t in use + // read in explicit exponent and calculate real exponent. + // if exponent is bogus (i.e. "1.234empty" or "1.234e+mpty") restore + // bogus exponent back onto returned string (endptr). - @param[in,out] endptr recieves a pointer to the first character after - the number + if (successful_parse and (*fst == 'e' or *fst == 'E')) + { + cp = *++fst; + bool negative_exp = cp == '-'; // read in exponent sign (+/-) - @return the floating point number - */ - float str_to_float_t(float* /* type */, char** endptr) const - { - return std::strtof(reinterpret_cast(m_start), endptr); + bool plus_or_minus = false; + if (cp == '-' or cp == '+') + { + cp = *++fst; + plus_or_minus = true; + } + + int count = 0; // exponent calculation + if (! nl_isdigit(cp)) + { + if (plus_or_minus) + { + *--fst; + } + + *--fst; + goto skip_loop; + } + + while (nl_isdigit(cp)) + { + constexpr int imax = std::numeric_limits::max(); + + if ((imax - std::abs(exp) - (cp - '0')) / 10 > count) + { + count *= 10; + count += cp - '0'; + } + else + { + count = imax - exp; + break; + } + + cp = *++fst; + } +skip_loop: + exp += negative_exp ? -count : count; + } + + // adjust number by powers of ten specified by format and exponent. + if (result != 0.0) + { + if (exp > std::numeric_limits::max_exponent10) + { + errno = ERANGE; + constexpr T inf = std::numeric_limits::infinity(); + result = (result < 0) ? -inf : inf; + } + else if (exp < std::numeric_limits::min_exponent10) + { + errno = ERANGE; + result = 0.0; + } + else if (exp < 0) + { + exp = -exp; + + for (std::size_t count = 0; exp; count++, exp >>= 1) + { + if (exp & 1) + { + result /= powerof10[count]; + } + } + } + else + { + for (std::size_t count = 0; exp; count++, exp >>= 1) + { + if (exp & 1) + { + result *= powerof10[count]; + } + } + } + } + + if (endptr != nullptr) + { + *endptr = const_cast(successful_parse ? fst : st); + } + + return result; } /*! @@ -8959,7 +9082,7 @@ class basic_json else { // parse with strtod - result.m_value.number_float = str_to_float_t(static_cast(nullptr), NULL); + result.m_value.number_float = strtox(reinterpret_cast(m_start), nullptr); } // save the type diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 656e13b319..911c6e5fd6 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8097,64 +8097,187 @@ class basic_json return result; } + // non locale aware isspace + bool nl_isspace(const char c) const + { + return + c == 0x20 or + c == 0x09 or + c == 0x0a or + c == 0x0b or + c == 0x0c or + c == 0x0d; + } + + // non locale aware isdigit + // Microsoft in 1252 codepage and others may classify additional single-byte characters as digits using std::isdigit + bool nl_isdigit(const char c) const + { + return c >= '0' and c <= '9'; + } + /*! - @brief parse floating point number + @brief parse string to floating point number + + This function is a reimplementation of the strtold family without + regard to locale - This function (and its overloads) serves to select the most approprate - standard floating point number parsing function based on the type - supplied via the first parameter. Set this to @a - static_cast(nullptr). + @tparam T a is_floating_point type - @param[in] type the @ref number_float_t in use + @param[in] st the string we will parse @param[in,out] endptr recieves a pointer to the first character after the number @return the floating point number */ - long double str_to_float_t(long double* /* type */, char** endptr) const + template ::value>::type> + T strtox(const char *st, char **endptr) const { - return std::strtold(reinterpret_cast(m_start), endptr); - } + constexpr std::array powerof10 { + {1.e1L, 1.e2L, 1.e4L, 1.e8L, 1.e16L, 1.e32L, 1.e64L, 1.e128L, 1.e256L} + }; - /*! - @brief parse floating point number + T result = 0; + const char *fst = st; + bool successful_parse = false; - This function (and its overloads) serves to select the most approprate - standard floating point number parsing function based on the type - supplied via the first parameter. Set this to @a - static_cast(nullptr). + while (nl_isspace(*fst)) + { + ++fst; + } - @param[in] type the @ref number_float_t in use + char cp = *fst; + int exp = 0; // exponent + { + const bool negative_sign = cp == '-'; - @param[in,out] endptr recieves a pointer to the first character after - the number + if (cp == '-' or cp == '+') + { + ++fst; + successful_parse = true; + } - @return the floating point number - */ - double str_to_float_t(double* /* type */, char** endptr) const - { - return std::strtod(reinterpret_cast(m_start), endptr); - } + // read in fractional part of number, until an 'e' is reached. + // count digits after decimal point. + while (nl_isdigit(cp = *fst)) + { + result = result * 10 + (cp - '0'); + successful_parse = true; + ++fst; + } - /*! - @brief parse floating point number + if (cp == '.') + { + while (nl_isdigit(cp = *++fst)) + { + result = result * 10 + (cp - '0'); + successful_parse = true; + --exp; + } + } - This function (and its overloads) serves to select the most approprate - standard floating point number parsing function based on the type - supplied via the first parameter. Set this to @a - static_cast(nullptr). + // if negative number, reverse sign + if (negative_sign) + { + result = -result; + } + } - @param[in] type the @ref number_float_t in use + // read in explicit exponent and calculate real exponent. + // if exponent is bogus (i.e. "1.234empty" or "1.234e+mpty") restore + // bogus exponent back onto returned string (endptr). - @param[in,out] endptr recieves a pointer to the first character after - the number + if (successful_parse and (*fst == 'e' or *fst == 'E')) + { + cp = *++fst; + bool negative_exp = cp == '-'; // read in exponent sign (+/-) - @return the floating point number - */ - float str_to_float_t(float* /* type */, char** endptr) const - { - return std::strtof(reinterpret_cast(m_start), endptr); + bool plus_or_minus = false; + if (cp == '-' or cp == '+') + { + cp = *++fst; + plus_or_minus = true; + } + + int count = 0; // exponent calculation + if (! nl_isdigit(cp)) + { + if (plus_or_minus) + { + *--fst; + } + + *--fst; + goto skip_loop; + } + + while (nl_isdigit(cp)) + { + constexpr int imax = std::numeric_limits::max(); + + if ((imax - std::abs(exp) - (cp - '0')) / 10 > count) + { + count *= 10; + count += cp - '0'; + } + else + { + count = imax - exp; + break; + } + + cp = *++fst; + } +skip_loop: + exp += negative_exp ? -count : count; + } + + // adjust number by powers of ten specified by format and exponent. + if (result != 0.0) + { + if (exp > std::numeric_limits::max_exponent10) + { + errno = ERANGE; + constexpr T inf = std::numeric_limits::infinity(); + result = (result < 0) ? -inf : inf; + } + else if (exp < std::numeric_limits::min_exponent10) + { + errno = ERANGE; + result = 0.0; + } + else if (exp < 0) + { + exp = -exp; + + for (std::size_t count = 0; exp; count++, exp >>= 1) + { + if (exp & 1) + { + result /= powerof10[count]; + } + } + } + else + { + for (std::size_t count = 0; exp; count++, exp >>= 1) + { + if (exp & 1) + { + result *= powerof10[count]; + } + } + } + } + + if (endptr != nullptr) + { + *endptr = const_cast(successful_parse ? fst : st); + } + + return result; } /*! @@ -8256,7 +8379,7 @@ class basic_json else { // parse with strtod - result.m_value.number_float = str_to_float_t(static_cast(nullptr), NULL); + result.m_value.number_float = strtox(reinterpret_cast(m_start), nullptr); } // save the type From 8877ad72fa34a316f875e00104d0672895705021 Mon Sep 17 00:00:00 2001 From: Jett Date: Sun, 16 Oct 2016 17:14:27 -0500 Subject: [PATCH 02/13] remove whitespace loop in strtox --- src/json.hpp | 18 ------------------ src/json.hpp.re2c | 18 ------------------ 2 files changed, 36 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 59b69294cc..143ac57321 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -8800,18 +8800,6 @@ class basic_json return result; } - // non locale aware isspace - bool nl_isspace(const char c) const - { - return - c == 0x20 or - c == 0x09 or - c == 0x0a or - c == 0x0b or - c == 0x0c or - c == 0x0d; - } - // non locale aware isdigit // Microsoft in 1252 codepage and others may classify additional single-byte characters as digits using std::isdigit bool nl_isdigit(const char c) const @@ -8845,12 +8833,6 @@ class basic_json T result = 0; const char *fst = st; bool successful_parse = false; - - while (nl_isspace(*fst)) - { - ++fst; - } - char cp = *fst; int exp = 0; // exponent { diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 911c6e5fd6..4f29a79e21 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8097,18 +8097,6 @@ class basic_json return result; } - // non locale aware isspace - bool nl_isspace(const char c) const - { - return - c == 0x20 or - c == 0x09 or - c == 0x0a or - c == 0x0b or - c == 0x0c or - c == 0x0d; - } - // non locale aware isdigit // Microsoft in 1252 codepage and others may classify additional single-byte characters as digits using std::isdigit bool nl_isdigit(const char c) const @@ -8142,12 +8130,6 @@ class basic_json T result = 0; const char *fst = st; bool successful_parse = false; - - while (nl_isspace(*fst)) - { - ++fst; - } - char cp = *fst; int exp = 0; // exponent { From 201e1f37fd7bedbbeb2a077c9caee3bec0d7a857 Mon Sep 17 00:00:00 2001 From: Jett Date: Sun, 16 Oct 2016 17:18:40 -0500 Subject: [PATCH 03/13] remove parse check for strtox --- src/json.hpp | 8 ++------ src/json.hpp.re2c | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 143ac57321..d61a52a362 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -8832,7 +8832,6 @@ class basic_json T result = 0; const char *fst = st; - bool successful_parse = false; char cp = *fst; int exp = 0; // exponent { @@ -8841,7 +8840,6 @@ class basic_json if (cp == '-' or cp == '+') { ++fst; - successful_parse = true; } // read in fractional part of number, until an 'e' is reached. @@ -8849,7 +8847,6 @@ class basic_json while (nl_isdigit(cp = *fst)) { result = result * 10 + (cp - '0'); - successful_parse = true; ++fst; } @@ -8858,7 +8855,6 @@ class basic_json while (nl_isdigit(cp = *++fst)) { result = result * 10 + (cp - '0'); - successful_parse = true; --exp; } } @@ -8874,7 +8870,7 @@ class basic_json // if exponent is bogus (i.e. "1.234empty" or "1.234e+mpty") restore // bogus exponent back onto returned string (endptr). - if (successful_parse and (*fst == 'e' or *fst == 'E')) + if (*fst == 'e' or *fst == 'E') { cp = *++fst; bool negative_exp = cp == '-'; // read in exponent sign (+/-) @@ -8959,7 +8955,7 @@ class basic_json if (endptr != nullptr) { - *endptr = const_cast(successful_parse ? fst : st); + *endptr = const_cast(fst); } return result; diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 4f29a79e21..3894058729 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8129,7 +8129,6 @@ class basic_json T result = 0; const char *fst = st; - bool successful_parse = false; char cp = *fst; int exp = 0; // exponent { @@ -8138,7 +8137,6 @@ class basic_json if (cp == '-' or cp == '+') { ++fst; - successful_parse = true; } // read in fractional part of number, until an 'e' is reached. @@ -8146,7 +8144,6 @@ class basic_json while (nl_isdigit(cp = *fst)) { result = result * 10 + (cp - '0'); - successful_parse = true; ++fst; } @@ -8155,7 +8152,6 @@ class basic_json while (nl_isdigit(cp = *++fst)) { result = result * 10 + (cp - '0'); - successful_parse = true; --exp; } } @@ -8171,7 +8167,7 @@ class basic_json // if exponent is bogus (i.e. "1.234empty" or "1.234e+mpty") restore // bogus exponent back onto returned string (endptr). - if (successful_parse and (*fst == 'e' or *fst == 'E')) + if (*fst == 'e' or *fst == 'E') { cp = *++fst; bool negative_exp = cp == '-'; // read in exponent sign (+/-) @@ -8256,7 +8252,7 @@ skip_loop: if (endptr != nullptr) { - *endptr = const_cast(successful_parse ? fst : st); + *endptr = const_cast(fst); } return result; From b7a978f7663888ee38790e80bad25ed56ad3ab79 Mon Sep 17 00:00:00 2001 From: Jett Date: Sun, 16 Oct 2016 17:26:11 -0500 Subject: [PATCH 04/13] remove endptr parameter from strtox --- src/json.hpp | 15 ++------------- src/json.hpp.re2c | 15 ++------------- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index d61a52a362..50cda78392 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -8817,14 +8817,11 @@ class basic_json @param[in] st the string we will parse - @param[in,out] endptr recieves a pointer to the first character after - the number - @return the floating point number */ template ::value>::type> - T strtox(const char *st, char **endptr) const + T strtox(const char *st) const { constexpr std::array powerof10 { {1.e1L, 1.e2L, 1.e4L, 1.e8L, 1.e16L, 1.e32L, 1.e64L, 1.e128L, 1.e256L} @@ -8867,9 +8864,6 @@ class basic_json } // read in explicit exponent and calculate real exponent. - // if exponent is bogus (i.e. "1.234empty" or "1.234e+mpty") restore - // bogus exponent back onto returned string (endptr). - if (*fst == 'e' or *fst == 'E') { cp = *++fst; @@ -8953,11 +8947,6 @@ class basic_json } } - if (endptr != nullptr) - { - *endptr = const_cast(fst); - } - return result; } @@ -9060,7 +9049,7 @@ class basic_json else { // parse with strtod - result.m_value.number_float = strtox(reinterpret_cast(m_start), nullptr); + result.m_value.number_float = strtox(reinterpret_cast(m_start)); } // save the type diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 3894058729..6ee8b81172 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8114,14 +8114,11 @@ class basic_json @param[in] st the string we will parse - @param[in,out] endptr recieves a pointer to the first character after - the number - @return the floating point number */ template ::value>::type> - T strtox(const char *st, char **endptr) const + T strtox(const char *st) const { constexpr std::array powerof10 { {1.e1L, 1.e2L, 1.e4L, 1.e8L, 1.e16L, 1.e32L, 1.e64L, 1.e128L, 1.e256L} @@ -8164,9 +8161,6 @@ class basic_json } // read in explicit exponent and calculate real exponent. - // if exponent is bogus (i.e. "1.234empty" or "1.234e+mpty") restore - // bogus exponent back onto returned string (endptr). - if (*fst == 'e' or *fst == 'E') { cp = *++fst; @@ -8250,11 +8244,6 @@ skip_loop: } } - if (endptr != nullptr) - { - *endptr = const_cast(fst); - } - return result; } @@ -8357,7 +8346,7 @@ skip_loop: else { // parse with strtod - result.m_value.number_float = strtox(reinterpret_cast(m_start), nullptr); + result.m_value.number_float = strtox(reinterpret_cast(m_start)); } // save the type From 7f5b228e2c566c707ea9b2f698a949a047de4422 Mon Sep 17 00:00:00 2001 From: Jett Date: Sun, 16 Oct 2016 17:28:26 -0500 Subject: [PATCH 05/13] remove unused errno setting from strtox --- src/json.hpp | 2 -- src/json.hpp.re2c | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 50cda78392..01433b3f34 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -8914,13 +8914,11 @@ class basic_json { if (exp > std::numeric_limits::max_exponent10) { - errno = ERANGE; constexpr T inf = std::numeric_limits::infinity(); result = (result < 0) ? -inf : inf; } else if (exp < std::numeric_limits::min_exponent10) { - errno = ERANGE; result = 0.0; } else if (exp < 0) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 6ee8b81172..4665198444 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8211,13 +8211,11 @@ skip_loop: { if (exp > std::numeric_limits::max_exponent10) { - errno = ERANGE; constexpr T inf = std::numeric_limits::infinity(); result = (result < 0) ? -inf : inf; } else if (exp < std::numeric_limits::min_exponent10) { - errno = ERANGE; result = 0.0; } else if (exp < 0) From 973ee97a93fb5683ad7698cf0ee840a948d36d8d Mon Sep 17 00:00:00 2001 From: Jett Date: Sun, 16 Oct 2016 17:35:27 -0500 Subject: [PATCH 06/13] remove unnecessary str pointer and rename arg to be clearer --- src/json.hpp | 27 +++++++++++++-------------- src/json.hpp.re2c | 25 ++++++++++++------------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 01433b3f34..7045a71028 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -8815,41 +8815,40 @@ class basic_json @tparam T a is_floating_point type - @param[in] st the string we will parse + @param[in] str the string we will parse @return the floating point number */ template ::value>::type> - T strtox(const char *st) const + T strtox(const char *str) const { constexpr std::array powerof10 { {1.e1L, 1.e2L, 1.e4L, 1.e8L, 1.e16L, 1.e32L, 1.e64L, 1.e128L, 1.e256L} }; T result = 0; - const char *fst = st; - char cp = *fst; + char cp = *str; int exp = 0; // exponent { const bool negative_sign = cp == '-'; if (cp == '-' or cp == '+') { - ++fst; + ++str; } // read in fractional part of number, until an 'e' is reached. // count digits after decimal point. - while (nl_isdigit(cp = *fst)) + while (nl_isdigit(cp = *str)) { result = result * 10 + (cp - '0'); - ++fst; + ++str; } if (cp == '.') { - while (nl_isdigit(cp = *++fst)) + while (nl_isdigit(cp = *++str)) { result = result * 10 + (cp - '0'); --exp; @@ -8864,15 +8863,15 @@ class basic_json } // read in explicit exponent and calculate real exponent. - if (*fst == 'e' or *fst == 'E') + if (*str == 'e' or *str == 'E') { - cp = *++fst; + cp = *++str; bool negative_exp = cp == '-'; // read in exponent sign (+/-) bool plus_or_minus = false; if (cp == '-' or cp == '+') { - cp = *++fst; + cp = *++str; plus_or_minus = true; } @@ -8881,10 +8880,10 @@ class basic_json { if (plus_or_minus) { - *--fst; + *--str; } - *--fst; + *--str; goto skip_loop; } @@ -8903,7 +8902,7 @@ class basic_json break; } - cp = *++fst; + cp = *++str; } skip_loop: exp += negative_exp ? -count : count; diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 4665198444..53fc2e0ed6 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8112,7 +8112,7 @@ class basic_json @tparam T a is_floating_point type - @param[in] st the string we will parse + @param[in] str the string we will parse @return the floating point number */ @@ -8125,28 +8125,27 @@ class basic_json }; T result = 0; - const char *fst = st; - char cp = *fst; + char cp = *str; int exp = 0; // exponent { const bool negative_sign = cp == '-'; if (cp == '-' or cp == '+') { - ++fst; + ++str; } // read in fractional part of number, until an 'e' is reached. // count digits after decimal point. - while (nl_isdigit(cp = *fst)) + while (nl_isdigit(cp = *str)) { result = result * 10 + (cp - '0'); - ++fst; + ++str; } if (cp == '.') { - while (nl_isdigit(cp = *++fst)) + while (nl_isdigit(cp = *++str)) { result = result * 10 + (cp - '0'); --exp; @@ -8161,15 +8160,15 @@ class basic_json } // read in explicit exponent and calculate real exponent. - if (*fst == 'e' or *fst == 'E') + if (*str == 'e' or *str == 'E') { - cp = *++fst; + cp = *++str; bool negative_exp = cp == '-'; // read in exponent sign (+/-) bool plus_or_minus = false; if (cp == '-' or cp == '+') { - cp = *++fst; + cp = *++str; plus_or_minus = true; } @@ -8178,10 +8177,10 @@ class basic_json { if (plus_or_minus) { - *--fst; + *--str; } - *--fst; + *--str; goto skip_loop; } @@ -8200,7 +8199,7 @@ class basic_json break; } - cp = *++fst; + cp = *++str; } skip_loop: exp += negative_exp ? -count : count; From 7dcc8a56bba738de6267dc4737e75ea68c144536 Mon Sep 17 00:00:00 2001 From: Jett Date: Thu, 20 Oct 2016 22:47:48 -0500 Subject: [PATCH 07/13] continuing work on our own strtold - reduced to 3 failing assertions this changes a few things. the name of the function to better represent what it does (string to json number) - we aren't strtoul compliant so best not to lie about that. we remove template code and just use one function. this makes it a bit simpler and we can always add it back if it buys us anything. the penalty for casting down from long double seems to be inconsequential. --- src/json.hpp | 37 ++++++++++++++++--------------------- src/json.hpp.re2c | 37 ++++++++++++++++--------------------- 2 files changed, 32 insertions(+), 42 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 7045a71028..149bedae44 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -8810,24 +8810,15 @@ class basic_json /*! @brief parse string to floating point number - This function is a reimplementation of the strtold family without - regard to locale - - @tparam T a is_floating_point type + This function is a partial reimplementation of the strtold in order to meet needs of JSON number @param[in] str the string we will parse @return the floating point number */ - template ::value>::type> - T strtox(const char *str) const + long double strtojnum(const char *str) const { - constexpr std::array powerof10 { - {1.e1L, 1.e2L, 1.e4L, 1.e8L, 1.e16L, 1.e32L, 1.e64L, 1.e128L, 1.e256L} - }; - - T result = 0; + long double result = 0; char cp = *str; int exp = 0; // exponent { @@ -8909,22 +8900,26 @@ class basic_json } // adjust number by powers of ten specified by format and exponent. - if (result != 0.0) + if (result != 0.0L) { - if (exp > std::numeric_limits::max_exponent10) + constexpr std::array powerof10 = { + {1.e1L, 1.e2L, 1.e4L, 1.e8L, 1.e16L, 1.e32L, 1.e64L, 1.e128L, 1.e256L} + }; + + if (exp > std::numeric_limits::max_exponent10) { - constexpr T inf = std::numeric_limits::infinity(); + constexpr long double inf = std::numeric_limits::infinity(); result = (result < 0) ? -inf : inf; } - else if (exp < std::numeric_limits::min_exponent10) + else if (exp < std::numeric_limits::min_exponent10) { - result = 0.0; + result = 0.0L; } else if (exp < 0) { exp = -exp; - for (std::size_t count = 0; exp; count++, exp >>= 1) + for (std::size_t count = 0; exp; ++count, exp >>= 1) { if (exp & 1) { @@ -8934,7 +8929,7 @@ class basic_json } else { - for (std::size_t count = 0; exp; count++, exp >>= 1) + for (std::size_t count = 0; exp; ++count, exp >>= 1) { if (exp & 1) { @@ -9045,8 +9040,8 @@ class basic_json } else { - // parse with strtod - result.m_value.number_float = strtox(reinterpret_cast(m_start)); + // convert string by json number format to floating point + result.m_value.number_float = strtojnum(reinterpret_cast(m_start)); } // save the type diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 53fc2e0ed6..ab0969350a 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8107,24 +8107,15 @@ class basic_json /*! @brief parse string to floating point number - This function is a reimplementation of the strtold family without - regard to locale - - @tparam T a is_floating_point type + This function is a partial reimplementation of the strtold in order to meet needs of JSON number @param[in] str the string we will parse @return the floating point number */ - template ::value>::type> - T strtox(const char *st) const + long double strtojnum(const char *str) const { - constexpr std::array powerof10 { - {1.e1L, 1.e2L, 1.e4L, 1.e8L, 1.e16L, 1.e32L, 1.e64L, 1.e128L, 1.e256L} - }; - - T result = 0; + long double result = 0; char cp = *str; int exp = 0; // exponent { @@ -8206,22 +8197,26 @@ skip_loop: } // adjust number by powers of ten specified by format and exponent. - if (result != 0.0) + if (result != 0.0L) { - if (exp > std::numeric_limits::max_exponent10) + constexpr std::array powerof10 = { + {1.e1L, 1.e2L, 1.e4L, 1.e8L, 1.e16L, 1.e32L, 1.e64L, 1.e128L, 1.e256L} + }; + + if (exp > std::numeric_limits::max_exponent10) { - constexpr T inf = std::numeric_limits::infinity(); + constexpr long double inf = std::numeric_limits::infinity(); result = (result < 0) ? -inf : inf; } - else if (exp < std::numeric_limits::min_exponent10) + else if (exp < std::numeric_limits::min_exponent10) { - result = 0.0; + result = 0.0L; } else if (exp < 0) { exp = -exp; - for (std::size_t count = 0; exp; count++, exp >>= 1) + for (std::size_t count = 0; exp; ++count, exp >>= 1) { if (exp & 1) { @@ -8231,7 +8226,7 @@ skip_loop: } else { - for (std::size_t count = 0; exp; count++, exp >>= 1) + for (std::size_t count = 0; exp; ++count, exp >>= 1) { if (exp & 1) { @@ -8342,8 +8337,8 @@ skip_loop: } else { - // parse with strtod - result.m_value.number_float = strtox(reinterpret_cast(m_start)); + // convert string by json number format to floating point + result.m_value.number_float = strtojnum(reinterpret_cast(m_start)); } // save the type From 7cc60ccc0a221b95761ab43aa522ce0fdafe86fa Mon Sep 17 00:00:00 2001 From: Jett Date: Thu, 20 Oct 2016 23:18:43 -0500 Subject: [PATCH 08/13] nl_isdigit can be made constexpr --- src/json.hpp | 2 +- src/json.hpp.re2c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 149bedae44..99561ea03a 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -8802,7 +8802,7 @@ class basic_json // non locale aware isdigit // Microsoft in 1252 codepage and others may classify additional single-byte characters as digits using std::isdigit - bool nl_isdigit(const char c) const + constexpr bool nl_isdigit(const char c) const { return c >= '0' and c <= '9'; } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index ab0969350a..166276c407 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8099,7 +8099,7 @@ class basic_json // non locale aware isdigit // Microsoft in 1252 codepage and others may classify additional single-byte characters as digits using std::isdigit - bool nl_isdigit(const char c) const + constexpr bool nl_isdigit(const char c) const { return c >= '0' and c <= '9'; } From dec60cb66ad951fe3f152a860d003a72bf367162 Mon Sep 17 00:00:00 2001 From: Jett Date: Thu, 20 Oct 2016 23:59:02 -0500 Subject: [PATCH 09/13] remove unneeded branch --- src/json.hpp | 51 ++++++++++++++++++++++------------------------- src/json.hpp.re2c | 51 ++++++++++++++++++++++------------------------- 2 files changed, 48 insertions(+), 54 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 99561ea03a..a96c98fc88 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -8900,41 +8900,38 @@ class basic_json } // adjust number by powers of ten specified by format and exponent. - if (result != 0.0L) + constexpr std::array powerof10 = { + {1.e1L, 1.e2L, 1.e4L, 1.e8L, 1.e16L, 1.e32L, 1.e64L, 1.e128L, 1.e256L} + }; + + if (exp > std::numeric_limits::max_exponent10) + { + constexpr long double inf = std::numeric_limits::infinity(); + result = (result < 0) ? -inf : inf; + } + else if (exp < std::numeric_limits::min_exponent10) + { + result = 0.0L; + } + else if (exp < 0) { - constexpr std::array powerof10 = { - {1.e1L, 1.e2L, 1.e4L, 1.e8L, 1.e16L, 1.e32L, 1.e64L, 1.e128L, 1.e256L} - }; + exp = -exp; - if (exp > std::numeric_limits::max_exponent10) - { - constexpr long double inf = std::numeric_limits::infinity(); - result = (result < 0) ? -inf : inf; - } - else if (exp < std::numeric_limits::min_exponent10) - { - result = 0.0L; - } - else if (exp < 0) + for (std::size_t count = 0; exp; ++count, exp >>= 1) { - exp = -exp; - - for (std::size_t count = 0; exp; ++count, exp >>= 1) + if (exp & 1) { - if (exp & 1) - { - result /= powerof10[count]; - } + result /= powerof10[count]; } } - else + } + else + { + for (std::size_t count = 0; exp; ++count, exp >>= 1) { - for (std::size_t count = 0; exp; ++count, exp >>= 1) + if (exp & 1) { - if (exp & 1) - { - result *= powerof10[count]; - } + result *= powerof10[count]; } } } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 166276c407..76b80978b1 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8197,41 +8197,38 @@ skip_loop: } // adjust number by powers of ten specified by format and exponent. - if (result != 0.0L) + constexpr std::array powerof10 = { + {1.e1L, 1.e2L, 1.e4L, 1.e8L, 1.e16L, 1.e32L, 1.e64L, 1.e128L, 1.e256L} + }; + + if (exp > std::numeric_limits::max_exponent10) + { + constexpr long double inf = std::numeric_limits::infinity(); + result = (result < 0) ? -inf : inf; + } + else if (exp < std::numeric_limits::min_exponent10) + { + result = 0.0L; + } + else if (exp < 0) { - constexpr std::array powerof10 = { - {1.e1L, 1.e2L, 1.e4L, 1.e8L, 1.e16L, 1.e32L, 1.e64L, 1.e128L, 1.e256L} - }; + exp = -exp; - if (exp > std::numeric_limits::max_exponent10) - { - constexpr long double inf = std::numeric_limits::infinity(); - result = (result < 0) ? -inf : inf; - } - else if (exp < std::numeric_limits::min_exponent10) - { - result = 0.0L; - } - else if (exp < 0) + for (std::size_t count = 0; exp; ++count, exp >>= 1) { - exp = -exp; - - for (std::size_t count = 0; exp; ++count, exp >>= 1) + if (exp & 1) { - if (exp & 1) - { - result /= powerof10[count]; - } + result /= powerof10[count]; } } - else + } + else + { + for (std::size_t count = 0; exp; ++count, exp >>= 1) { - for (std::size_t count = 0; exp; ++count, exp >>= 1) + if (exp & 1) { - if (exp & 1) - { - result *= powerof10[count]; - } + result *= powerof10[count]; } } } From babb65df46351ac8c7d3fbf3591add791cdba2d4 Mon Sep 17 00:00:00 2001 From: Jett Date: Fri, 21 Oct 2016 00:09:31 -0500 Subject: [PATCH 10/13] different neg style for consistency --- src/json.hpp | 4 ++-- src/json.hpp.re2c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index a96c98fc88..06e71e3646 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -8849,7 +8849,7 @@ class basic_json // if negative number, reverse sign if (negative_sign) { - result = -result; + result *= -1; } } @@ -8915,7 +8915,7 @@ class basic_json } else if (exp < 0) { - exp = -exp; + exp *= -1; for (std::size_t count = 0; exp; ++count, exp >>= 1) { diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 76b80978b1..c678718e30 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8146,7 +8146,7 @@ class basic_json // if negative number, reverse sign if (negative_sign) { - result = -result; + result *= -1; } } @@ -8212,7 +8212,7 @@ skip_loop: } else if (exp < 0) { - exp = -exp; + exp *= -1; for (std::size_t count = 0; exp; ++count, exp >>= 1) { From 308082a9a8f0e1c963de640c90b7bec7d393e36e Mon Sep 17 00:00:00 2001 From: Jett Date: Sun, 23 Oct 2016 14:55:14 -0500 Subject: [PATCH 11/13] make negative_exp and plus_or_minus const --- src/json.hpp | 7 +++---- src/json.hpp.re2c | 8 +++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 06e71e3646..78ede7e75c 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -8857,13 +8857,12 @@ class basic_json if (*str == 'e' or *str == 'E') { cp = *++str; - bool negative_exp = cp == '-'; // read in exponent sign (+/-) - bool plus_or_minus = false; - if (cp == '-' or cp == '+') + const bool negative_exp = cp == '-'; // read in exponent sign (+/-) + const bool plus_or_minus = (cp == '-' or cp == '+'); + if (plus_or_minus) { cp = *++str; - plus_or_minus = true; } int count = 0; // exponent calculation diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index c678718e30..a1b81efd03 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8154,13 +8154,11 @@ class basic_json if (*str == 'e' or *str == 'E') { cp = *++str; - bool negative_exp = cp == '-'; // read in exponent sign (+/-) - - bool plus_or_minus = false; - if (cp == '-' or cp == '+') + const bool negative_exp = cp == '-'; // read in exponent sign (+/-) + const bool plus_or_minus = (cp == '-' or cp == '+'); + if (plus_or_minus) { cp = *++str; - plus_or_minus = true; } int count = 0; // exponent calculation From 66a4c06a9d7122083dd2ecee1fe7474442ed50aa Mon Sep 17 00:00:00 2001 From: Jett Date: Sun, 23 Oct 2016 14:58:12 -0500 Subject: [PATCH 12/13] use not over ! --- src/json.hpp | 2 +- src/json.hpp.re2c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 78ede7e75c..12fbcf5b0a 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -8866,7 +8866,7 @@ class basic_json } int count = 0; // exponent calculation - if (! nl_isdigit(cp)) + if (not nl_isdigit(cp)) { if (plus_or_minus) { diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index a1b81efd03..0014de271c 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8154,6 +8154,7 @@ class basic_json if (*str == 'e' or *str == 'E') { cp = *++str; + const bool negative_exp = cp == '-'; // read in exponent sign (+/-) const bool plus_or_minus = (cp == '-' or cp == '+'); if (plus_or_minus) @@ -8162,7 +8163,7 @@ class basic_json } int count = 0; // exponent calculation - if (! nl_isdigit(cp)) + if (not nl_isdigit(cp)) { if (plus_or_minus) { From bc53b798e610831a2b06ee1779dd4973b649fa06 Mon Sep 17 00:00:00 2001 From: Jett Date: Sun, 23 Oct 2016 18:01:40 -0500 Subject: [PATCH 13/13] add additional comments --- src/json.hpp | 7 +++++++ src/json.hpp.re2c | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/json.hpp b/src/json.hpp index 12fbcf5b0a..0509e45649 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -8903,19 +8903,24 @@ class basic_json {1.e1L, 1.e2L, 1.e4L, 1.e8L, 1.e16L, 1.e32L, 1.e64L, 1.e128L, 1.e256L} }; + // round to INF if our exponent is larger than representable number if (exp > std::numeric_limits::max_exponent10) { constexpr long double inf = std::numeric_limits::infinity(); result = (result < 0) ? -inf : inf; } + // round to zero if our exponent is smaller than representable number else if (exp < std::numeric_limits::min_exponent10) { result = 0.0L; } + // iteratively divide result for negative exp else if (exp < 0) { + // make exp positive for loop below exp *= -1; + // check enabled exp bits on lookup powerof10 lookup table for (std::size_t count = 0; exp; ++count, exp >>= 1) { if (exp & 1) @@ -8924,8 +8929,10 @@ class basic_json } } } + // iteratively multiply result for positive exp else { + // check enabled exp bits on lookup powerof10 lookup table for (std::size_t count = 0; exp; ++count, exp >>= 1) { if (exp & 1) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 0014de271c..99631bbae2 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8200,19 +8200,24 @@ skip_loop: {1.e1L, 1.e2L, 1.e4L, 1.e8L, 1.e16L, 1.e32L, 1.e64L, 1.e128L, 1.e256L} }; + // round to INF if our exponent is larger than representable number if (exp > std::numeric_limits::max_exponent10) { constexpr long double inf = std::numeric_limits::infinity(); result = (result < 0) ? -inf : inf; } + // round to zero if our exponent is smaller than representable number else if (exp < std::numeric_limits::min_exponent10) { result = 0.0L; } + // iteratively divide result for negative exp else if (exp < 0) { + // make exp positive for loop below exp *= -1; + // check enabled exp bits on lookup powerof10 lookup table for (std::size_t count = 0; exp; ++count, exp >>= 1) { if (exp & 1) @@ -8221,8 +8226,10 @@ skip_loop: } } } + // iteratively multiply result for positive exp else { + // check enabled exp bits on lookup powerof10 lookup table for (std::size_t count = 0; exp; ++count, exp >>= 1) { if (exp & 1)