new strtod/ld/f function

This commit is contained in:
Jett 2016-10-16 07:07:28 -05:00
parent 60bba02cc6
commit 22f3f88771
2 changed files with 324 additions and 78 deletions

View File

@ -8800,64 +8800,187 @@ basic_json_parser_63:
return result; return result;
} }
/*! // non locale aware isspace
@brief parse floating point number bool nl_isspace(const char c) const
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<number_float_t*>(nullptr).
@param[in] type the @ref number_float_t in use
@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
{ {
return std::strtold(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); 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 (and its overloads) serves to select the most approprate This function is a reimplementation of the strtold family without
standard floating point number parsing function based on the type regard to locale
supplied via the first parameter. Set this to @a
static_cast<number_float_t*>(nullptr).
@param[in] type the @ref number_float_t in use @tparam T a is_floating_point type
@param[in] st the string we will parse
@param[in,out] endptr recieves a pointer to the first character after @param[in,out] endptr recieves a pointer to the first character after
the number the number
@return the floating point number @return the floating point number
*/ */
double str_to_float_t(double* /* type */, char** endptr) const template <typename T, typename = typename std::enable_if<
std::is_floating_point<T>::value>::type>
T strtox(const char *st, char **endptr) const
{ {
return std::strtod(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); constexpr std::array<long double, 9> 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;
bool successful_parse = false;
while (nl_isspace(*fst))
{
++fst;
} }
/*! char cp = *fst;
@brief parse floating point number int exp = 0; // exponent
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<number_float_t*>(nullptr).
@param[in] type the @ref number_float_t in use
@param[in,out] endptr recieves a pointer to the first character after
the number
@return the floating point number
*/
float str_to_float_t(float* /* type */, char** endptr) const
{ {
return std::strtof(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); const bool negative_sign = cp == '-';
if (cp == '-' or cp == '+')
{
++fst;
successful_parse = true;
}
// 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;
}
if (cp == '.')
{
while (nl_isdigit(cp = *++fst))
{
result = result * 10 + (cp - '0');
successful_parse = true;
--exp;
}
}
// if negative number, reverse sign
if (negative_sign)
{
result = -result;
}
}
// 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 (successful_parse and (*fst == 'e' or *fst == 'E'))
{
cp = *++fst;
bool negative_exp = cp == '-'; // read in exponent sign (+/-)
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<int>::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<T>::max_exponent10)
{
errno = ERANGE;
constexpr T inf = std::numeric_limits<T>::infinity();
result = (result < 0) ? -inf : inf;
}
else if (exp < std::numeric_limits<T>::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<char *>(successful_parse ? fst : st);
}
return result;
} }
/*! /*!
@ -8959,7 +9082,7 @@ basic_json_parser_63:
else else
{ {
// parse with strtod // parse with strtod
result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), NULL); result.m_value.number_float = strtox<number_float_t>(reinterpret_cast<typename string_t::const_pointer>(m_start), nullptr);
} }
// save the type // save the type

View File

@ -8097,64 +8097,187 @@ class basic_json
return result; return result;
} }
/*! // non locale aware isspace
@brief parse floating point number bool nl_isspace(const char c) const
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<number_float_t*>(nullptr).
@param[in] type the @ref number_float_t in use
@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
{ {
return std::strtold(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); 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 (and its overloads) serves to select the most approprate This function is a reimplementation of the strtold family without
standard floating point number parsing function based on the type regard to locale
supplied via the first parameter. Set this to @a
static_cast<number_float_t*>(nullptr).
@param[in] type the @ref number_float_t in use @tparam T a is_floating_point type
@param[in] st the string we will parse
@param[in,out] endptr recieves a pointer to the first character after @param[in,out] endptr recieves a pointer to the first character after
the number the number
@return the floating point number @return the floating point number
*/ */
double str_to_float_t(double* /* type */, char** endptr) const template <typename T, typename = typename std::enable_if<
std::is_floating_point<T>::value>::type>
T strtox(const char *st, char **endptr) const
{ {
return std::strtod(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); constexpr std::array<long double, 9> 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;
bool successful_parse = false;
while (nl_isspace(*fst))
{
++fst;
} }
/*! char cp = *fst;
@brief parse floating point number int exp = 0; // exponent
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<number_float_t*>(nullptr).
@param[in] type the @ref number_float_t in use
@param[in,out] endptr recieves a pointer to the first character after
the number
@return the floating point number
*/
float str_to_float_t(float* /* type */, char** endptr) const
{ {
return std::strtof(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); const bool negative_sign = cp == '-';
if (cp == '-' or cp == '+')
{
++fst;
successful_parse = true;
}
// 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;
}
if (cp == '.')
{
while (nl_isdigit(cp = *++fst))
{
result = result * 10 + (cp - '0');
successful_parse = true;
--exp;
}
}
// if negative number, reverse sign
if (negative_sign)
{
result = -result;
}
}
// 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 (successful_parse and (*fst == 'e' or *fst == 'E'))
{
cp = *++fst;
bool negative_exp = cp == '-'; // read in exponent sign (+/-)
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<int>::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<T>::max_exponent10)
{
errno = ERANGE;
constexpr T inf = std::numeric_limits<T>::infinity();
result = (result < 0) ? -inf : inf;
}
else if (exp < std::numeric_limits<T>::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<char *>(successful_parse ? fst : st);
}
return result;
} }
/*! /*!
@ -8256,7 +8379,7 @@ class basic_json
else else
{ {
// parse with strtod // parse with strtod
result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), NULL); result.m_value.number_float = strtox<number_float_t>(reinterpret_cast<typename string_t::const_pointer>(m_start), nullptr);
} }
// save the type // save the type