rationalize/
lib.rs

1use num::rational::Ratio;
2
3/// Expand a float to a continued fraction expansion
4///
5/// # Arguments
6///
7/// * `n`:  The float to expand
8/// * `min`:  Numbers less than this value are treated as zero
9///
10/// returns: [usize; N] where N is the number of terms in the expansion
11///
12/// # Examples
13///
14/// ```
15/// # use rationalize::continued_fraction_expansion;
16/// let cfe = continued_fraction_expansion::<10>(std::f64::consts::PI, 1e-10);
17/// assert_eq!(cfe, [3, 7, 15, 1, 292, 1, 1, 1, 2, 1]);
18/// ```
19pub fn continued_fraction_expansion<const N: usize>(n: f64, min: f64) -> [usize; N] {
20    let mut out = [0; N];
21    let mut n = n;
22    for i in 0..N {
23        let integer = n.floor() as usize;
24        out[i] = integer;
25        n -= integer as f64;
26        if n <= min {
27            break;
28        }
29        n = 1.0 / n;
30    }
31    out
32}
33
34/// Convert a float to a rational number
35///
36/// # Arguments
37///
38/// * `n`:  The float to convert
39/// * `min`:  Numbers less than this value are treated as zero
40///
41/// returns: Ratio<usize>
42///
43/// # Examples
44///
45/// ```
46/// use num::rational::Ratio;
47/// use rationalize::float2ratio;
48/// let cfe = float2ratio::<4>(std::f64::consts::PI, 1e-10);
49/// assert_eq!(cfe, Ratio::new(355, 113));
50/// ```
51pub fn float2ratio<const N: usize>(n: f64, min: f64) -> Ratio<isize> {
52    match n.is_sign_negative() {
53        true => {
54            let cfe = continued_fraction_expansion::<N>(-n, min);
55            let (numer, denom) = build_ratio(&cfe).into();
56            -Ratio::new(numer as isize, denom as isize)
57        }
58        false => {
59            let cfe = continued_fraction_expansion::<N>(n, min);
60            let (numer, denom) = build_ratio(&cfe).into();
61            Ratio::new(numer as isize, denom as isize)
62        }
63    }
64}
65
66// build ratio from continued fraction expansion
67pub fn build_ratio(cfe: &[usize]) -> Ratio<usize> {
68    let seq = trim_tail_zeros(cfe);
69    let mut out = Ratio::new(seq[seq.len() - 1], 1);
70    for i in (0..seq.len() - 1).rev() {
71        out = Ratio::new(seq[i], 1) + out.recip();
72    }
73    out
74}
75
76pub fn trim_tail_zeros(cfe: &[usize]) -> &[usize] {
77    let mut i = cfe.len() - 1;
78    while i > 0 && cfe[i] == 0 {
79        i -= 1;
80    }
81    &cfe[0..=i]
82}