fn parse_e_notation()

in arrow-cast/src/parse.rs [732:850]


fn parse_e_notation<T: DecimalType>(
    s: &str,
    mut digits: u16,
    mut fractionals: i16,
    mut result: T::Native,
    index: usize,
    precision: u16,
    scale: i16,
) -> Result<T::Native, ArrowError> {
    let mut exp: i16 = 0;
    let base = T::Native::usize_as(10);

    let mut exp_start: bool = false;
    // e has a plus sign
    let mut pos_shift_direction: bool = true;

    // skip to point or exponent index
    let mut bs;
    if fractionals > 0 {
        // it's a fraction, so the point index needs to be skipped, so +1
        bs = s.as_bytes().iter().skip(index + fractionals as usize + 1);
    } else {
        // it's actually an integer that is already written into the result, so let's skip on to e
        bs = s.as_bytes().iter().skip(index);
    }

    while let Some(b) = bs.next() {
        match b {
            b'0'..=b'9' => {
                result = result.mul_wrapping(base);
                result = result.add_wrapping(T::Native::usize_as((b - b'0') as usize));
                if fractionals > 0 {
                    fractionals += 1;
                }
                digits += 1;
            }
            &b'e' | &b'E' => {
                exp_start = true;
            }
            _ => {
                return Err(ArrowError::ParseError(format!(
                    "can't parse the string value {s} to decimal"
                )));
            }
        };

        if exp_start {
            pos_shift_direction = match bs.next() {
                Some(&b'-') => false,
                Some(&b'+') => true,
                Some(b) => {
                    if !b.is_ascii_digit() {
                        return Err(ArrowError::ParseError(format!(
                            "can't parse the string value {s} to decimal"
                        )));
                    }

                    exp *= 10;
                    exp += (b - b'0') as i16;

                    true
                }
                None => {
                    return Err(ArrowError::ParseError(format!(
                        "can't parse the string value {s} to decimal"
                    )))
                }
            };

            for b in bs.by_ref() {
                if !b.is_ascii_digit() {
                    return Err(ArrowError::ParseError(format!(
                        "can't parse the string value {s} to decimal"
                    )));
                }
                exp *= 10;
                exp += (b - b'0') as i16;
            }
        }
    }

    if digits == 0 && fractionals == 0 && exp == 0 {
        return Err(ArrowError::ParseError(format!(
            "can't parse the string value {s} to decimal"
        )));
    }

    if !pos_shift_direction {
        // exponent has a large negative sign
        // 1.12345e-30 => 0.0{29}12345, scale = 5
        if exp - (digits as i16 + scale) > 0 {
            return Ok(T::Native::usize_as(0));
        }
        exp *= -1;
    }

    // point offset
    exp = fractionals - exp;
    // We have zeros on the left, we need to count them
    if !pos_shift_direction && exp > digits as i16 {
        digits = exp as u16;
    }
    // Number of numbers to be removed or added
    exp = scale - exp;

    if (digits as i16 + exp) as u16 > precision {
        return Err(ArrowError::ParseError(format!(
            "parse decimal overflow ({s})"
        )));
    }

    if exp < 0 {
        result = result.div_wrapping(base.pow_wrapping(-exp as _));
    } else {
        result = result.mul_wrapping(base.pow_wrapping(exp as _));
    }

    Ok(result)
}