|
14 | 14 | use std::cmp;
|
15 | 15 | use std::from_str::FromStr;
|
16 | 16 | use std::num::{Zero,One,ToStrRadix,FromStrRadix,Round};
|
17 |
| -use super::bigint::BigInt; |
| 17 | +use super::bigint::{BigInt, BigUint, Sign, Plus, Minus}; |
18 | 18 |
|
19 | 19 | /// Represents the ratio between 2 numbers.
|
20 | 20 | #[deriving(Clone)]
|
@@ -107,6 +107,27 @@ impl<T: Clone + Integer + Ord>
|
107 | 107 | }
|
108 | 108 | }
|
109 | 109 |
|
| 110 | +impl Ratio<BigInt> { |
| 111 | + /// Converts a float into a rational number |
| 112 | + pub fn from_float<T: Float>(f: T) -> Option<BigRational> { |
| 113 | + if !f.is_finite() { |
| 114 | + return None; |
| 115 | + } |
| 116 | + let (mantissa, exponent, sign) = f.integer_decode(); |
| 117 | + let bigint_sign: Sign = if sign == 1 { Plus } else { Minus }; |
| 118 | + if exponent < 0 { |
| 119 | + let one: BigInt = One::one(); |
| 120 | + let denom: BigInt = one << ((-exponent) as uint); |
| 121 | + let numer: BigUint = FromPrimitive::from_u64(mantissa).unwrap(); |
| 122 | + Some(Ratio::new(BigInt::from_biguint(bigint_sign, numer), denom)) |
| 123 | + } else { |
| 124 | + let mut numer: BigUint = FromPrimitive::from_u64(mantissa).unwrap(); |
| 125 | + numer = numer << (exponent as uint); |
| 126 | + Some(Ratio::from_integer(BigInt::from_biguint(bigint_sign, numer))) |
| 127 | + } |
| 128 | + } |
| 129 | +} |
| 130 | + |
110 | 131 | /* Comparisons */
|
111 | 132 |
|
112 | 133 | // comparing a/b and c/d is the same as comparing a*d and b*c, so we
|
@@ -621,4 +642,42 @@ mod test {
|
621 | 642 | test(s);
|
622 | 643 | }
|
623 | 644 | }
|
| 645 | + |
| 646 | + #[test] |
| 647 | + fn test_from_float() { |
| 648 | + fn test<T: Float>(given: T, (numer, denom): (&str, &str)) { |
| 649 | + let ratio: BigRational = Ratio::from_float(given).unwrap(); |
| 650 | + assert_eq!(ratio, Ratio::new( |
| 651 | + FromStr::from_str(numer).unwrap(), |
| 652 | + FromStr::from_str(denom).unwrap())); |
| 653 | + } |
| 654 | + |
| 655 | + // f32 |
| 656 | + test(3.14159265359f32, ("13176795", "4194304")); |
| 657 | + test(2f32.pow(&100.), ("1267650600228229401496703205376", "1")); |
| 658 | + test(-2f32.pow(&100.), ("-1267650600228229401496703205376", "1")); |
| 659 | + test(1.0 / 2f32.pow(&100.), ("1", "1267650600228229401496703205376")); |
| 660 | + test(684729.48391f32, ("1369459", "2")); |
| 661 | + test(-8573.5918555f32, ("-4389679", "512")); |
| 662 | + |
| 663 | + // f64 |
| 664 | + test(3.14159265359f64, ("3537118876014453", "1125899906842624")); |
| 665 | + test(2f64.pow(&100.), ("1267650600228229401496703205376", "1")); |
| 666 | + test(-2f64.pow(&100.), ("-1267650600228229401496703205376", "1")); |
| 667 | + test(684729.48391f64, ("367611342500051", "536870912")); |
| 668 | + test(-8573.5918555, ("-4713381968463931", "549755813888")); |
| 669 | + test(1.0 / 2f64.pow(&100.), ("1", "1267650600228229401496703205376")); |
| 670 | + } |
| 671 | + |
| 672 | + #[test] |
| 673 | + fn test_from_float_fail() { |
| 674 | + use std::{f32, f64}; |
| 675 | + |
| 676 | + assert_eq!(Ratio::from_float(f32::NAN), None); |
| 677 | + assert_eq!(Ratio::from_float(f32::INFINITY), None); |
| 678 | + assert_eq!(Ratio::from_float(f32::NEG_INFINITY), None); |
| 679 | + assert_eq!(Ratio::from_float(f64::NAN), None); |
| 680 | + assert_eq!(Ratio::from_float(f64::INFINITY), None); |
| 681 | + assert_eq!(Ratio::from_float(f64::NEG_INFINITY), None); |
| 682 | + } |
624 | 683 | }
|
0 commit comments