How to convert a decimal number to a rational number in Rust

1 Answer

0 votes
use num_bigint::BigInt;
use num_integer::Integer;

/*
    DecimalToRational (Rust)
    ------------------------
    Converts a decimal number (given as a string) into an exact rational p/q.

    Why parse the string manually?
        • Rust has no built‑in rational type in std.
        • f64 cannot preserve exact decimal digits.
        • Using BigInt ensures perfect accuracy.
        • The num crate provides BigInt + gcd helpers.

    Algorithm:
        1. Look for a decimal point.
        2. If none → integer → numerator = n, denominator = 1.
        3. Otherwise:
              Example: "12.345"
              integer part   = 12
              fractional part = 345
              digits = 3

              numerator   = integer_part * 10^digits + fractional_part
              denominator = 10^digits

        4. Reduce using gcd.
*/

#[derive(Debug)]
struct Rational {
    numerator: BigInt,
    denominator: BigInt,
}

fn convert_decimal_to_rational(s: &str) -> Rational {
    if let Some(dot_pos) = s.find('.') {
        // Split into integer and fractional parts
        let int_part: &str = &s[..dot_pos];
        let frac_part: &str = &s[dot_pos + 1..];

        let integer_value: BigInt = int_part.parse().unwrap();
        let fractional_value: BigInt = frac_part.parse().unwrap();

        let digits: usize = frac_part.len();

        // denominator = 10^digits
        let denominator: BigInt = BigInt::from(10u32).pow(digits as u32);

        // numerator = integerValue * denominator + fractionalValue
        let numerator: BigInt = integer_value * &denominator + fractional_value;

        // Reduce using gcd
        let g: BigInt = numerator.gcd(&denominator);
        let numerator = numerator / &g;
        let denominator = denominator / &g;

        Rational { numerator, denominator }
    } else {
        // No decimal point → integer
        let numerator: BigInt = s.parse().unwrap();
        let denominator: BigInt = BigInt::from(1);
        Rational { numerator, denominator }
    }
}

fn main() {
    let values: [&str; 8] = [
        "3.5", "12.75", "0.125", "100.001",
        "7", "42.0", "0.333", "5.2",
    ];

    for v in values {
        let r: Rational = convert_decimal_to_rational(v);
        println!("{} -> {}/{}", v, r.numerator, r.denominator);
    }
}



/*
run:

3.5 -> 7/2
12.75 -> 51/4
0.125 -> 1/8
100.001 -> 100001/1000
7 -> 7/1
42.0 -> 42/1
0.333 -> 333/1000
5.2 -> 26/5

*/

 



answered Jul 23 by avibootz

Related questions

...