strings_internal::ParsedFloat ParseFloat()

in absl/strings/internal/charconv_parse.cc [355:495]


strings_internal::ParsedFloat ParseFloat(const char* begin, const char* end,
                                         chars_format format_flags) {
  strings_internal::ParsedFloat result;

  // Exit early if we're given an empty range.
  if (begin == end) return result;

  // Handle the infinity and NaN cases.
  if (ParseInfinityOrNan(begin, end, &result)) {
    return result;
  }

  const char* const mantissa_begin = begin;
  while (begin < end && *begin == '0') {
    ++begin;  // skip leading zeros
  }
  uint64_t mantissa = 0;

  int exponent_adjustment = 0;
  bool mantissa_is_inexact = false;
  int pre_decimal_digits = ConsumeDigits<base>(
      begin, end, MantissaDigitsMax<base>(), &mantissa, &mantissa_is_inexact);
  begin += pre_decimal_digits;
  int digits_left;
  if (pre_decimal_digits >= DigitLimit<base>()) {
    // refuse to parse pathological inputs
    return result;
  } else if (pre_decimal_digits > MantissaDigitsMax<base>()) {
    // We dropped some non-fraction digits on the floor.  Adjust our exponent
    // to compensate.
    exponent_adjustment =
        static_cast<int>(pre_decimal_digits - MantissaDigitsMax<base>());
    digits_left = 0;
  } else {
    digits_left =
        static_cast<int>(MantissaDigitsMax<base>() - pre_decimal_digits);
  }
  if (begin < end && *begin == '.') {
    ++begin;
    if (mantissa == 0) {
      // If we haven't seen any nonzero digits yet, keep skipping zeros.  We
      // have to adjust the exponent to reflect the changed place value.
      const char* begin_zeros = begin;
      while (begin < end && *begin == '0') {
        ++begin;
      }
      int zeros_skipped = static_cast<int>(begin - begin_zeros);
      if (zeros_skipped >= DigitLimit<base>()) {
        // refuse to parse pathological inputs
        return result;
      }
      exponent_adjustment -= static_cast<int>(zeros_skipped);
    }
    int post_decimal_digits = ConsumeDigits<base>(
        begin, end, digits_left, &mantissa, &mantissa_is_inexact);
    begin += post_decimal_digits;

    // Since `mantissa` is an integer, each significant digit we read after
    // the decimal point requires an adjustment to the exponent. "1.23e0" will
    // be stored as `mantissa` == 123 and `exponent` == -2 (that is,
    // "123e-2").
    if (post_decimal_digits >= DigitLimit<base>()) {
      // refuse to parse pathological inputs
      return result;
    } else if (post_decimal_digits > digits_left) {
      exponent_adjustment -= digits_left;
    } else {
      exponent_adjustment -= post_decimal_digits;
    }
  }
  // If we've found no mantissa whatsoever, this isn't a number.
  if (mantissa_begin == begin) {
    return result;
  }
  // A bare "." doesn't count as a mantissa either.
  if (begin - mantissa_begin == 1 && *mantissa_begin == '.') {
    return result;
  }

  if (mantissa_is_inexact) {
    // We dropped significant digits on the floor.  Handle this appropriately.
    if (base == 10) {
      // If we truncated significant decimal digits, store the full range of the
      // mantissa for future big integer math for exact rounding.
      result.subrange_begin = mantissa_begin;
      result.subrange_end = begin;
    } else if (base == 16) {
      // If we truncated hex digits, reflect this fact by setting the low
      // ("sticky") bit.  This allows for correct rounding in all cases.
      mantissa |= 1;
    }
  }
  result.mantissa = mantissa;

  const char* const exponent_begin = begin;
  result.literal_exponent = 0;
  bool found_exponent = false;
  if (AllowExponent(format_flags) && begin < end &&
      IsExponentCharacter<base>(*begin)) {
    bool negative_exponent = false;
    ++begin;
    if (begin < end && *begin == '-') {
      negative_exponent = true;
      ++begin;
    } else if (begin < end && *begin == '+') {
      ++begin;
    }
    const char* const exponent_digits_begin = begin;
    // Exponent is always expressed in decimal, even for hexadecimal floats.
    begin += ConsumeDigits<10>(begin, end, kDecimalExponentDigitsMax,
                               &result.literal_exponent, nullptr);
    if (begin == exponent_digits_begin) {
      // there were no digits where we expected an exponent.  We failed to read
      // an exponent and should not consume the 'e' after all.  Rewind 'begin'.
      found_exponent = false;
      begin = exponent_begin;
    } else {
      found_exponent = true;
      if (negative_exponent) {
        result.literal_exponent = -result.literal_exponent;
      }
    }
  }

  if (!found_exponent && RequireExponent(format_flags)) {
    // Provided flags required an exponent, but none was found.  This results
    // in a failure to scan.
    return result;
  }

  // Success!
  result.type = strings_internal::FloatType::kNumber;
  if (result.mantissa > 0) {
    result.exponent = result.literal_exponent +
                      (DigitMagnitude<base>() * exponent_adjustment);
  } else {
    result.exponent = 0;
  }
  result.end = begin;
  return result;
}