rust-num / num-rational

Generic Rational numbers for Rust
Apache License 2.0
143 stars 50 forks source link

Add the ability to parse decimals into `Ratio` #131

Open tgross35 opened 4 months ago

tgross35 commented 4 months ago

Would it be possible to parse decimals into rationals? Python has a way to do this to their Fraction, And Julia has a roundabout way to do this.

E.g. let x: BigRational = "1234.45e67".parse().unwrap()

Discussion about the inverse: https://github.com/rust-num/num-rational/issues/10

tgross35 commented 4 months ago

Helper I am using to do this for BigRational:

fn parse_rational(s: &str) -> BigRational {
    let mut s = s; // lifetime rules

    // Fast path; no decimals or exponents ot parse
    if s.bytes().all(|b| b.is_ascii_digit() || b == b'-') {
        return BigRational::from_str(s).unwrap();
    }

    let mut ten_exp: i32 = 0;

    // Remove and handle e.g. `e-4`, `e+10`, `e5` suffixes
    if let Some(pos) = s.bytes().position(|b| b == b'e') {
        let (dec, exp) = s.split_at(pos);
        s = dec;
        ten_exp = exp[1..].parse().unwrap();
    }

    // Remove the decimal and instead change our exponent
    // E.g. "12.3456" becomes "123456 * 10^-4"
    let mut s_owned;
    if let Some(pos) = s.bytes().position(|b| b == b'.') {
        ten_exp = ten_exp
            .checked_sub((s.len() - pos - 1).try_into().unwrap())
            .unwrap();
        s_owned = s.to_owned();
        s_owned.remove(pos);
        s = &s_owned;
    }

    let mut r = BigRational::from_str(s).unwrap();
    r *= BigRational::from_u32(10).unwrap().pow(ten_exp);
    r
}

This algorithm is simple but seems like it would run into overflow issues easily with sized ratios.