Parsing floats with 19 significant digits should be fine. (#1191)

* Parsing floats with 19 significant digits should be fine.

* Adding more tests with very long mantissa.
This commit is contained in:
Daniel Lemire 2020-09-29 19:42:43 -04:00 committed by GitHub
parent da093c1982
commit 8b5a89c136
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 47 additions and 6 deletions

View File

@ -396,7 +396,9 @@ simdjson_really_inline error_code write_float(const uint8_t *const src, bool neg
// we could extend our code by using a 128-bit integer instead
// of a 64-bit integer. However, this is uncommon in practice.
// digit count is off by 1 because of the decimal (assuming there was one).
if (simdjson_unlikely(digit_count-1 >= 19 && significant_digits(start_digits, digit_count) >= 19)) {
//
// 9999999999999999999 < 2**64 so we can accomodate 19 digits.
if (simdjson_unlikely(digit_count-1 > 19 && significant_digits(start_digits, digit_count) > 19)) {
// Ok, chances are good that we had an overflow!
// this is almost never going to get called!!!
// we start anew, going slowly!!!
@ -715,16 +717,16 @@ SIMDJSON_UNUSED simdjson_really_inline simdjson_result<double> parse_double(cons
while (parse_digit(*p, i)) { p++; }
exponent = -(p - start_decimal_digits);
// Overflow check. 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 >= 19;
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = start_digits-src >= 19;
overflow = start_digits-src > 19;
}
} else {
overflow = p-src >= 19;
overflow = p-src > 19;
}
//

View File

@ -52,6 +52,44 @@ namespace number_tests {
return true;
}
bool nines() {
std::cout << __func__ << std::endl;
simdjson::dom::parser parser;
std::vector<std::pair<std::string, double>> testing = {
{"9999999999999999999.0",9999999999999999999.0},
{"9999999999999999999",9999999999999999999.},
{"999999999999999999.9",999999999999999999.9},
{"99999999999999999.99",99999999999999999.99},
{"9999999999999999.999",9999999999999999.999},
{"999999999999999.9999",999999999999999.9999},
{"99999999999999.99999",99999999999999.99999},
{"9999999999999.999999",9999999999999.999999},
{"999999999999.9999999",999999999999.9999999},
{"99999999999.99999999",99999999999.99999999},
{"9999999999.999999999",9999999999.999999999},
{"999999999.9999999999",999999999.9999999999},
{"99999999.99999999999",99999999.99999999999},
{"9999999.999999999999",9999999.999999999999},
{"999999.9999999999999",999999.9999999999999},
{"99999.99999999999999",99999.99999999999999},
{"9999.999999999999999",9999.999999999999999},
{"999.9999999999999999",999.9999999999999999},
{"99.99999999999999999",99.99999999999999999},
{"9.999999999999999999",9.999999999999999999},
{"0.9999999999999999999",0.9999999999999999999},
{"0.09999999999999999999",0.09999999999999999999},
};
for (std::pair<std::string, double> p : testing) {
double actual;
ASSERT_SUCCESS(parser.parse(p.first).get(actual));
if (actual != p.second) {
std::cerr << "JSON '" << p.first << "' parsed to " << actual << " instead of " << p.first << std::endl;
return false;
}
}
return true;
}
bool powers_of_two() {
std::cout << __func__ << std::endl;
char buf[1024];
@ -182,7 +220,8 @@ namespace number_tests {
bool run() {
return small_integers() &&
powers_of_two() &&
powers_of_ten();
powers_of_ten() &&
nines();
}
}