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
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 (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
*/ */
long double str_to_float_t(long 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::strtold(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;
@brief parse floating point number const char *fst = st;
bool successful_parse = false;
This function (and its overloads) serves to select the most approprate while (nl_isspace(*fst))
standard floating point number parsing function based on the type {
supplied via the first parameter. Set this to @a ++fst;
static_cast<number_float_t*>(nullptr). }
@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 if (cp == '-' or cp == '+')
the number {
++fst;
successful_parse = true;
}
@return the floating point number // read in fractional part of number, until an 'e' is reached.
*/ // count digits after decimal point.
double str_to_float_t(double* /* type */, char** endptr) const while (nl_isdigit(cp = *fst))
{ {
return std::strtod(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); result = result * 10 + (cp - '0');
} successful_parse = true;
++fst;
}
/*! if (cp == '.')
@brief parse floating point number {
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 // if negative number, reverse sign
standard floating point number parsing function based on the type if (negative_sign)
supplied via the first parameter. Set this to @a {
static_cast<number_float_t*>(nullptr). 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 if (successful_parse and (*fst == 'e' or *fst == 'E'))
the number {
cp = *++fst;
bool negative_exp = cp == '-'; // read in exponent sign (+/-)
@return the floating point number bool plus_or_minus = false;
*/ if (cp == '-' or cp == '+')
float str_to_float_t(float* /* type */, char** endptr) const {
{ cp = *++fst;
return std::strtof(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); 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
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 (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
*/ */
long double str_to_float_t(long 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::strtold(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;
@brief parse floating point number const char *fst = st;
bool successful_parse = false;
This function (and its overloads) serves to select the most approprate while (nl_isspace(*fst))
standard floating point number parsing function based on the type {
supplied via the first parameter. Set this to @a ++fst;
static_cast<number_float_t*>(nullptr). }
@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 if (cp == '-' or cp == '+')
the number {
++fst;
successful_parse = true;
}
@return the floating point number // read in fractional part of number, until an 'e' is reached.
*/ // count digits after decimal point.
double str_to_float_t(double* /* type */, char** endptr) const while (nl_isdigit(cp = *fst))
{ {
return std::strtod(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); result = result * 10 + (cp - '0');
} successful_parse = true;
++fst;
}
/*! if (cp == '.')
@brief parse floating point number {
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 // if negative number, reverse sign
standard floating point number parsing function based on the type if (negative_sign)
supplied via the first parameter. Set this to @a {
static_cast<number_float_t*>(nullptr). 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 if (successful_parse and (*fst == 'e' or *fst == 'E'))
the number {
cp = *++fst;
bool negative_exp = cp == '-'; // read in exponent sign (+/-)
@return the floating point number bool plus_or_minus = false;
*/ if (cp == '-' or cp == '+')
float str_to_float_t(float* /* type */, char** endptr) const {
{ cp = *++fst;
return std::strtof(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); 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