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}