mirror of
https://github.com/fastfloat/fast_float.git
synced 2025-12-07 01:06:48 +08:00
Merge pull request #220 from mayawarrier/main
Add support for parsing numbers according to JSON format
This commit is contained in:
commit
e6b370dc2c
@ -280,7 +280,7 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
|
|||||||
answer.too_many_digits = false;
|
answer.too_many_digits = false;
|
||||||
answer.negative = (*p == UC('-'));
|
answer.negative = (*p == UC('-'));
|
||||||
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
|
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
|
||||||
if ((*p == UC('-')) || (*p == UC('+'))) {
|
if ((*p == UC('-')) || (!(fmt & FASTFLOAT_JSONFMT) && *p == UC('+'))) {
|
||||||
#else
|
#else
|
||||||
if (*p == UC('-')) { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
|
if (*p == UC('-')) { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
|
||||||
#endif
|
#endif
|
||||||
@ -288,10 +288,16 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
|
|||||||
if (p == pend) {
|
if (p == pend) {
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
if (fmt & FASTFLOAT_JSONFMT) {
|
||||||
|
if (!is_integer(*p)) { // a sign must be followed by an integer
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot
|
if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
UC const * const start_digits = p;
|
UC const * const start_digits = p;
|
||||||
|
|
||||||
uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
|
uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
|
||||||
@ -306,8 +312,16 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
|
|||||||
UC const * const end_of_integer_part = p;
|
UC const * const end_of_integer_part = p;
|
||||||
int64_t digit_count = int64_t(end_of_integer_part - start_digits);
|
int64_t digit_count = int64_t(end_of_integer_part - start_digits);
|
||||||
answer.integer = span<const UC>(start_digits, size_t(digit_count));
|
answer.integer = span<const UC>(start_digits, size_t(digit_count));
|
||||||
|
if (fmt & FASTFLOAT_JSONFMT) {
|
||||||
|
// at least 1 digit in integer part, without leading zeros
|
||||||
|
if (digit_count == 0 || (start_digits[0] == UC('0') && digit_count > 1)) {
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int64_t exponent = 0;
|
int64_t exponent = 0;
|
||||||
if ((p != pend) && (*p == decimal_point)) {
|
const bool has_decimal_point = (p != pend) && (*p == decimal_point);
|
||||||
|
if (has_decimal_point) {
|
||||||
++p;
|
++p;
|
||||||
UC const * before = p;
|
UC const * before = p;
|
||||||
// can occur at most twice without overflowing, but let it occur more, since
|
// can occur at most twice without overflowing, but let it occur more, since
|
||||||
@ -323,8 +337,13 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
|
|||||||
answer.fraction = span<const UC>(before, size_t(p - before));
|
answer.fraction = span<const UC>(before, size_t(p - before));
|
||||||
digit_count -= exponent;
|
digit_count -= exponent;
|
||||||
}
|
}
|
||||||
// we must have encountered at least one integer!
|
if (fmt & FASTFLOAT_JSONFMT) {
|
||||||
if (digit_count == 0) {
|
// at least 1 digit in fractional part
|
||||||
|
if (has_decimal_point && exponent == 0) {
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (digit_count == 0) { // we must have encountered at least one integer!
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
int64_t exp_number = 0; // explicit exponential part
|
int64_t exp_number = 0; // explicit exponential part
|
||||||
|
|||||||
@ -12,10 +12,15 @@
|
|||||||
|
|
||||||
namespace fast_float {
|
namespace fast_float {
|
||||||
|
|
||||||
|
#define FASTFLOAT_JSONFMT (1 << 5)
|
||||||
|
|
||||||
enum chars_format {
|
enum chars_format {
|
||||||
scientific = 1 << 0,
|
scientific = 1 << 0,
|
||||||
fixed = 1 << 2,
|
fixed = 1 << 2,
|
||||||
hex = 1 << 3,
|
hex = 1 << 3,
|
||||||
|
no_infnan = 1 << 4,
|
||||||
|
json = FASTFLOAT_JSONFMT | fixed | scientific | no_infnan,
|
||||||
|
json_or_infnan = FASTFLOAT_JSONFMT | fixed | scientific,
|
||||||
general = fixed | scientific
|
general = fixed | scientific
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -164,8 +164,14 @@ from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
|
|||||||
}
|
}
|
||||||
parsed_number_string_t<UC> pns = parse_number_string<UC>(first, last, options);
|
parsed_number_string_t<UC> pns = parse_number_string<UC>(first, last, options);
|
||||||
if (!pns.valid) {
|
if (!pns.valid) {
|
||||||
|
if (options.format & chars_format::no_infnan) {
|
||||||
|
answer.ec = std::errc::invalid_argument;
|
||||||
|
answer.ptr = first;
|
||||||
|
return answer;
|
||||||
|
} else {
|
||||||
return detail::parse_infnan(first, last, value);
|
return detail::parse_infnan(first, last, value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
answer.ec = std::errc(); // be optimistic
|
answer.ec = std::errc(); // be optimistic
|
||||||
answer.ptr = pns.lastmatch;
|
answer.ptr = pns.lastmatch;
|
||||||
|
|||||||
@ -72,6 +72,8 @@ fast_float_add_cpp_test(long_test)
|
|||||||
fast_float_add_cpp_test(powersoffive_hardround)
|
fast_float_add_cpp_test(powersoffive_hardround)
|
||||||
fast_float_add_cpp_test(string_test)
|
fast_float_add_cpp_test(string_test)
|
||||||
|
|
||||||
|
fast_float_add_cpp_test(json_fmt)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
option(FASTFLOAT_EXHAUSTIVE "Exhaustive tests" OFF)
|
option(FASTFLOAT_EXHAUSTIVE "Exhaustive tests" OFF)
|
||||||
|
|||||||
40
tests/json_fmt.cpp
Normal file
40
tests/json_fmt.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// test that this option is ignored
|
||||||
|
#define FASTFLOAT_ALLOWS_LEADING_PLUS
|
||||||
|
|
||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
const std::vector<double> expected{ -0.2, 0.02, 0.002, 1., 0., std::numeric_limits<double>::infinity() };
|
||||||
|
const std::vector<std::string> accept{ "-0.2", "0.02", "0.002", "1e+0000", "0e-2", "inf" };
|
||||||
|
const std::vector<std::string> reject{ "-.2", "00.02", "0.e+1", "00.e+1", ".25", "+0.25", "inf", "nan(snan)" };
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < accept.size(); ++i)
|
||||||
|
{
|
||||||
|
const auto& f = accept[i];
|
||||||
|
double result;
|
||||||
|
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, fast_float::chars_format::json_or_infnan);
|
||||||
|
if (answer.ec != std::errc() || result != expected[i]) {
|
||||||
|
std::cerr << "json fmt rejected valid json " << f << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < reject.size(); ++i)
|
||||||
|
{
|
||||||
|
const auto& f = reject[i];
|
||||||
|
double result;
|
||||||
|
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, fast_float::chars_format::json);
|
||||||
|
if (answer.ec == std::errc()) {
|
||||||
|
std::cerr << "json fmt accepted invalid json " << f << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user