From a27fcc230db3e8148683a2fd544af1c61aa02810 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Thu, 7 Jan 2021 17:46:47 -0500 Subject: [PATCH 1/4] This should be mostly correct. --- include/fast_float/ascii_number.h | 31 +++++++-- include/fast_float/float_common.h | 5 +- include/fast_float/parse_number.h | 109 +++++++++++++++++++++++++----- 3 files changed, 123 insertions(+), 22 deletions(-) diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index db9b8d62..59de8551 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h @@ -60,6 +60,7 @@ fastfloat_really_inline parsed_number_string parse_number_string(const char *p, const char *pend, chars_format fmt) noexcept { parsed_number_string answer; answer.valid = false; + answer.too_many_digits = false; answer.negative = (*p == '-'); if ((*p == '-') || (*p == '+')) { ++p; @@ -81,6 +82,8 @@ parsed_number_string parse_number_string(const char *p, const char *pend, chars_ uint64_t(*p - '0'); // might overflow, we will handle the overflow later ++p; } + const char *const end_of_integer_part = p; + int64_t exponent = 0; if ((p != pend) && (*p == '.')) { ++p; @@ -111,9 +114,9 @@ parsed_number_string parse_number_string(const char *p, const char *pend, chars_ int32_t digit_count = int32_t(p - start_digits); // used later to guard against overflows if(exponent > 0) {digit_count--;} + int64_t exp_number = 0; // explicit exponential part if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) { const char * location_of_e = p; - int64_t exp_number = 0; // exponential part ++p; bool neg_exp = false; if ((p != pend) && ('-' == *p)) { @@ -137,7 +140,8 @@ parsed_number_string parse_number_string(const char *p, const char *pend, chars_ } ++p; } - exponent += (neg_exp ? -exp_number : exp_number); + if(neg_exp) { exp_number = - exp_number; } + exponent += exp_number; } } else { // If it scientific and not fixed, we have to bail out. @@ -164,12 +168,29 @@ parsed_number_string parse_number_string(const char *p, const char *pend, chars_ // We over-decrement by one when there is a decimal separator digit_count -= int(start - start_digits); if (digit_count > 19) { - answer.mantissa = 0xFFFFFFFFFFFFFFFF; // important: we don't want the mantissa to be used in a fast path uninitialized. answer.too_many_digits = true; - return answer; + // Let us start again, this time, avoiding overflows. + i = 0; + p = start_digits; + const uint64_t minimal_nineteen_digit_integer{1000000000000000000}; + while((i < minimal_nineteen_digit_integer) && (p != pend) && is_integer(*p)) { + i = i * 10 + uint64_t(*p - '0'); + ++p; + } + if (i >= minimal_nineteen_digit_integer) { // We have a big integers + exponent = end_of_integer_part - p + exp_number; + } else { // We have a value with a fractional component. + p++; // skip the '.' + const char *first_after_period = p; + while((i < minimal_nineteen_digit_integer) && (p != pend) && is_integer(*p)) { + i = i * 10 + uint64_t(*p - '0'); + ++p; + } + exponent = first_after_period - p + exp_number; + } + // We have now corrected both exponent and i, to a truncated value. } } - answer.too_many_digits = false; answer.exponent = exponent; answer.mantissa = i; return answer; diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index 8c6fdbf1..b4a7741f 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -184,6 +184,9 @@ struct adjusted_mantissa { bool operator==(const adjusted_mantissa &o) const { return mantissa == o.mantissa && power2 == o.power2; } + bool operator!=(const adjusted_mantissa &o) const { + return mantissa != o.mantissa || power2 != o.power2; + } }; struct decimal { @@ -372,4 +375,4 @@ inline OStream& operator<<(OStream &out, const fast_float::decimal &d) { return out; } -#endif +#endif \ No newline at end of file diff --git a/include/fast_float/parse_number.h b/include/fast_float/parse_number.h index 45714090..36771747 100644 --- a/include/fast_float/parse_number.h +++ b/include/fast_float/parse_number.h @@ -66,6 +66,25 @@ from_chars_result parse_infnan(const char *first, const char *last, T &value) n answer.ptr = first; return answer; } + +template +fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) { + uint64_t word = am.mantissa; + word |= uint64_t(am.power2) << binary_format::mantissa_explicit_bits(); + word = negative + ? word | (uint64_t(1) << binary_format::sign_index()) : word; +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + if (std::is_same::value) { + ::memcpy(&value, (char *)&word + 4, sizeof(T)); // extract value at offset 4-7 if float on big-endian + } else { + ::memcpy(&value, &word, sizeof(T)); + } +#else + // For little-endian systems: + ::memcpy(&value, &word, sizeof(T)); +#endif +} + } // namespace @@ -92,31 +111,89 @@ from_chars_result from_chars(const char *first, const char *last, answer.ec = std::errc(); // be optimistic answer.ptr = pns.lastmatch; // Next is Clinger's fast path. - if (binary_format::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format::max_exponent_fast_path() && pns.mantissa <=binary_format::max_mantissa_fast_path()) { + if (binary_format::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format::max_exponent_fast_path() && pns.mantissa <=binary_format::max_mantissa_fast_path() && !pns.too_many_digits) { value = T(pns.mantissa); if (pns.exponent < 0) { value = value / binary_format::exact_power_of_ten(-pns.exponent); } else { value = value * binary_format::exact_power_of_ten(pns.exponent); } if (pns.negative) { value = -value; } return answer; } - adjusted_mantissa am = pns.too_many_digits ? parse_long_mantissa>(first,last) : compute_float>(pns.exponent, pns.mantissa); + adjusted_mantissa am = compute_float>(pns.exponent, pns.mantissa); + if(pns.too_many_digits) { + if(am != compute_float>(pns.exponent, pns.mantissa + 1)) { + am.power2 = -1; // value is invalid. + } + } // If we called compute_float>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0), // then we need to go the long way around again. This is very uncommon. if(am.power2 < 0) { am = parse_long_mantissa>(first,last); } - uint64_t word = am.mantissa; - word |= uint64_t(am.power2) << binary_format::mantissa_explicit_bits(); - word = pns.negative - ? word | (uint64_t(1) << binary_format::sign_index()) : word; -#if FASTFLOAT_IS_BIG_ENDIAN == 1 - if (std::is_same::value) { - ::memcpy(&value, (char *)&word + 4, sizeof(T)); // extract value at offset 4-7 if float on big-endian - } else { - ::memcpy(&value, &word, sizeof(T)); - } -#else - // For little-endian systems: - ::memcpy(&value, &word, sizeof(T)); -#endif + to_float(pns.negative, am, value); + return answer; +} + +template +from_chars_result odlfrom_chars(const char *first, const char *last, + T &value, chars_format fmt /*= chars_format::general*/) noexcept { + static_assert (std::is_same::value || std::is_same::value, "only float and double are supported"); + + + from_chars_result answer; + while ((first != last) && fast_float::is_space(uint8_t(*first))) { + first++; + } + if (first == last) { + answer.ec = std::errc::invalid_argument; + answer.ptr = first; + return answer; + } + parsed_number_string pns = parse_number_string(first, last, fmt); + if (!pns.valid) { + return parse_infnan(first, last, value); + } + answer.ec = std::errc(); // be optimistic + answer.ptr = pns.lastmatch; + adjusted_mantissa am; + // Most times, we have pns.too_many_digits = false. + if(pns.too_many_digits) { + // Uncommon path where we have too many digits. + // + // credit: R. Oudompheng who first implemented this fast path. + // It does the job of accelerating the slow path since most + // long streams of digits are determined after 19 digits. + // Note that mantissa+1 cannot overflow since mantissa < 10**19 and so + // mantissa+1 <= 10**19 < 2**64. + adjusted_mantissa am1 = compute_float>(pns.exponent, pns.mantissa); + adjusted_mantissa am2 = compute_float>(pns.exponent, pns.mantissa+1); + // They must both agree and be both a successful result. + if(( am1 == am2 ) && (am1.power2 >= 0)) { + am = am1; + } else { + // long way! (uncommon) + decimal d = parse_decimal(first, last); + am = compute_float>(d); + } + to_float(pns.negative, am, value); + } else { + // We are entering the common path where the number of digits is no more than 19. + // + // Next is Clinger's fast path. + if (binary_format::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format::max_exponent_fast_path() && pns.mantissa <=binary_format::max_mantissa_fast_path()) { + value = T(pns.mantissa); + if (pns.exponent < 0) { value = value / binary_format::exact_power_of_ten(-pns.exponent); } + else { value = value * binary_format::exact_power_of_ten(pns.exponent); } + if (pns.negative) { value = -value; } + return answer; + } + // Then we have our main routine. + am = compute_float>(pns.exponent, pns.mantissa); + // If we called compute_float>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0), + // then we need to go the long way around again. This is very uncommon. + if(am.power2 < 0) { // long way! (uncommon) + decimal d = parse_decimal(first, last); + am = compute_float>(d); + } + to_float(pns.negative, am, value); + } return answer; } From cad8cfdf5740bc8d4f99d83c5f24a303e2738720 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Thu, 7 Jan 2021 18:01:57 -0500 Subject: [PATCH 2/4] Removing dead code. --- include/fast_float/parse_number.h | 66 ------------------------------- 1 file changed, 66 deletions(-) diff --git a/include/fast_float/parse_number.h b/include/fast_float/parse_number.h index 36771747..f69b7418 100644 --- a/include/fast_float/parse_number.h +++ b/include/fast_float/parse_number.h @@ -131,72 +131,6 @@ from_chars_result from_chars(const char *first, const char *last, return answer; } -template -from_chars_result odlfrom_chars(const char *first, const char *last, - T &value, chars_format fmt /*= chars_format::general*/) noexcept { - static_assert (std::is_same::value || std::is_same::value, "only float and double are supported"); - - - from_chars_result answer; - while ((first != last) && fast_float::is_space(uint8_t(*first))) { - first++; - } - if (first == last) { - answer.ec = std::errc::invalid_argument; - answer.ptr = first; - return answer; - } - parsed_number_string pns = parse_number_string(first, last, fmt); - if (!pns.valid) { - return parse_infnan(first, last, value); - } - answer.ec = std::errc(); // be optimistic - answer.ptr = pns.lastmatch; - adjusted_mantissa am; - // Most times, we have pns.too_many_digits = false. - if(pns.too_many_digits) { - // Uncommon path where we have too many digits. - // - // credit: R. Oudompheng who first implemented this fast path. - // It does the job of accelerating the slow path since most - // long streams of digits are determined after 19 digits. - // Note that mantissa+1 cannot overflow since mantissa < 10**19 and so - // mantissa+1 <= 10**19 < 2**64. - adjusted_mantissa am1 = compute_float>(pns.exponent, pns.mantissa); - adjusted_mantissa am2 = compute_float>(pns.exponent, pns.mantissa+1); - // They must both agree and be both a successful result. - if(( am1 == am2 ) && (am1.power2 >= 0)) { - am = am1; - } else { - // long way! (uncommon) - decimal d = parse_decimal(first, last); - am = compute_float>(d); - } - to_float(pns.negative, am, value); - } else { - // We are entering the common path where the number of digits is no more than 19. - // - // Next is Clinger's fast path. - if (binary_format::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format::max_exponent_fast_path() && pns.mantissa <=binary_format::max_mantissa_fast_path()) { - value = T(pns.mantissa); - if (pns.exponent < 0) { value = value / binary_format::exact_power_of_ten(-pns.exponent); } - else { value = value * binary_format::exact_power_of_ten(pns.exponent); } - if (pns.negative) { value = -value; } - return answer; - } - // Then we have our main routine. - am = compute_float>(pns.exponent, pns.mantissa); - // If we called compute_float>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0), - // then we need to go the long way around again. This is very uncommon. - if(am.power2 < 0) { // long way! (uncommon) - decimal d = parse_decimal(first, last); - am = compute_float>(d); - } - to_float(pns.negative, am, value); - } - return answer; -} - } // namespace fast_float #endif From 192b271c128eac0e48186e6dbb7901d42423b30f Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Thu, 7 Jan 2021 18:03:33 -0500 Subject: [PATCH 3/4] Removing dead code --- include/fast_float/float_common.h | 38 ------------------- .../fast_float/simple_decimal_conversion.h | 13 ------- 2 files changed, 51 deletions(-) diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index b4a7741f..cb15ac23 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -203,44 +203,6 @@ struct decimal { // Moves are allowed: decimal(decimal &&) = default; decimal &operator=(decimal &&other) = default; - // Generates a mantissa by truncating to 19 digits. - // This function should be reasonably fast. - // Note that the user is responsible to ensure that digits are - // initialized to zero when there are fewer than 19. - inline uint64_t to_truncated_mantissa() { -#if FASTFLOAT_IS_BIG_ENDIAN == 1 - uint64_t mantissa = 0; - for (uint32_t i = 0; i < max_digit_without_overflow; - i++) { - mantissa = mantissa * 10 + digits[i]; // can be accelerated - } - return mantissa; -#else - uint64_t val; - // 8 first digits - ::memcpy(&val, digits, sizeof(uint64_t)); - val = val * 2561 >> 8; - val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16; - uint64_t mantissa = - uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32); - // 8 more digits for a total of 16 - ::memcpy(&val, digits + sizeof(uint64_t), sizeof(uint64_t)); - val = val * 2561 >> 8; - val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16; - uint32_t eight_digits_value = - uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32); - mantissa = 100000000 * mantissa + eight_digits_value; - for (uint32_t i = 2 * sizeof(uint64_t); i < max_digit_without_overflow; - i++) { - mantissa = mantissa * 10 + digits[i]; // can be accelerated - } - return mantissa; -#endif - } - // Generate an exponent matching to_truncated_mantissa() - inline int32_t to_truncated_exponent() { - return decimal_point - int32_t(max_digit_without_overflow); - } }; constexpr static double powers_of_ten_double[] = { diff --git a/include/fast_float/simple_decimal_conversion.h b/include/fast_float/simple_decimal_conversion.h index 1d6fda1f..bfb60b66 100644 --- a/include/fast_float/simple_decimal_conversion.h +++ b/include/fast_float/simple_decimal_conversion.h @@ -353,19 +353,6 @@ adjusted_mantissa compute_float(decimal &d) { template adjusted_mantissa parse_long_mantissa(const char *first, const char* last) { decimal d = parse_decimal(first, last); - // In some cases we can get lucky and looking at only the first 19 digits is enough. - // Let us try that. - const uint64_t mantissa = d.to_truncated_mantissa(); - const int64_t exponent = d.to_truncated_exponent(); - // credit: R. Oudompheng who first implemented this fast path (to my knowledge). - // It is rough, but it does the job of accelerating the slow path since most - // long streams of digits are determined after 19 digits. - // Note that mantissa+1 cannot overflow since mantissa < 10**19 and so - // mantissa+1 <= 10**19 < 2**64. - adjusted_mantissa am1 = compute_float(exponent, mantissa); - adjusted_mantissa am2 = compute_float(exponent, mantissa+1); - // They must both agree and be both a successful result. - if(( am1 == am2 ) && (am1.power2 >= 0)) { return am1; } return compute_float(d); } From cf1a4ec2f568830831f78d03e2b6f0f85e7623d8 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Fri, 8 Jan 2021 10:09:26 -0500 Subject: [PATCH 4/4] Further tweaking. --- include/fast_float/ascii_number.h | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index 59de8551..75432a3f 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h @@ -83,11 +83,10 @@ parsed_number_string parse_number_string(const char *p, const char *pend, chars_ ++p; } const char *const end_of_integer_part = p; - + int64_t digit_count = int64_t(end_of_integer_part - start_digits); int64_t exponent = 0; if ((p != pend) && (*p == '.')) { ++p; - const char *first_after_period = p; #if FASTFLOAT_IS_BIG_ENDIAN == 0 // Fast approach only tested under little endian systems if ((p + 8 <= pend) && is_made_of_eight_digits_fast(p)) { @@ -104,16 +103,13 @@ parsed_number_string parse_number_string(const char *p, const char *pend, chars_ ++p; i = i * 10 + digit; // in rare cases, this will overflow, but that's ok } - exponent = first_after_period - p; + exponent = end_of_integer_part + 1 - p; + digit_count -= exponent; } // we must have encountered at least one integer! - if ((start_digits == p) || ((start_digits == p - 1) && (*start_digits == '.') )) { + if (digit_count == 0) { return answer; } - // digit_count is the exact number of digits. - int32_t digit_count = - int32_t(p - start_digits); // used later to guard against overflows - if(exponent > 0) {digit_count--;} int64_t exp_number = 0; // explicit exponential part if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) { const char * location_of_e = p; @@ -155,18 +151,16 @@ parsed_number_string parse_number_string(const char *p, const char *pend, chars_ // of a 64-bit integer. However, this is uncommon. // // We can deal with up to 19 digits. - if (((digit_count > 19))) { // this is uncommon + if (digit_count > 19) { // this is uncommon // It is possible that the integer had an overflow. // We have to handle the case where we have 0.0000somenumber. // We need to be mindful of the case where we only have zeroes... // E.g., 0.000000000...000. const char *start = start_digits; while ((start != pend) && (*start == '0' || *start == '.')) { - if(*start == '.') { digit_count++; } // We will subtract it again later. + if(*start == '0') { digit_count --; } start++; } - // We over-decrement by one when there is a decimal separator - digit_count -= int(start - start_digits); if (digit_count > 19) { answer.too_many_digits = true; // Let us start again, this time, avoiding overflows. @@ -188,7 +182,7 @@ parsed_number_string parse_number_string(const char *p, const char *pend, chars_ } exponent = first_after_period - p + exp_number; } - // We have now corrected both exponent and i, to a truncated value. + // We have now corrected both exponent and i, to a truncated value } } answer.exponent = exponent;