about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-12-30 14:31:57 -0800
committerbors <bors@rust-lang.org>2013-12-30 14:31:57 -0800
commitdf25bb65eb56aa3815226c74190079be3904c13c (patch)
treef1d24e017ba5e26e610023a28c4ace72fe3e16b1 /src
parent1502b1197b78624aad5180fa8eff3f77f6f0abfc (diff)
parente0a6910f7092ffff33331a08f2fafb55eed46835 (diff)
downloadrust-df25bb65eb56aa3815226c74190079be3904c13c.tar.gz
rust-df25bb65eb56aa3815226c74190079be3904c13c.zip
auto merge of #11125 : vmx/rust/rational, r=huonw
The Ratio::rational() converts a float (f32 and f64) into a
Ratio<BigInt>.

Closes #9838
Diffstat (limited to 'src')
-rw-r--r--src/libextra/num/rational.rs61
1 files changed, 60 insertions, 1 deletions
diff --git a/src/libextra/num/rational.rs b/src/libextra/num/rational.rs
index 21f905e772e..3ab9f99d5cf 100644
--- a/src/libextra/num/rational.rs
+++ b/src/libextra/num/rational.rs
@@ -14,7 +14,7 @@
 use std::cmp;
 use std::from_str::FromStr;
 use std::num::{Zero,One,ToStrRadix,FromStrRadix,Round};
-use super::bigint::BigInt;
+use super::bigint::{BigInt, BigUint, Sign, Plus, Minus};
 
 /// Represents the ratio between 2 numbers.
 #[deriving(Clone)]
@@ -107,6 +107,27 @@ impl<T: Clone + Integer + Ord>
     }
 }
 
+impl Ratio<BigInt> {
+    /// Converts a float into a rational number
+    pub fn from_float<T: Float>(f: T) -> Option<BigRational> {
+        if !f.is_finite() {
+            return None;
+        }
+        let (mantissa, exponent, sign) = f.integer_decode();
+        let bigint_sign: Sign = if sign == 1 { Plus } else { Minus };
+        if exponent < 0 {
+            let one: BigInt = One::one();
+            let denom: BigInt = one << ((-exponent) as uint);
+            let numer: BigUint = FromPrimitive::from_u64(mantissa).unwrap();
+            Some(Ratio::new(BigInt::from_biguint(bigint_sign, numer), denom))
+        } else {
+            let mut numer: BigUint = FromPrimitive::from_u64(mantissa).unwrap();
+            numer = numer << (exponent as uint);
+            Some(Ratio::from_integer(BigInt::from_biguint(bigint_sign, numer)))
+        }
+    }
+}
+
 /* Comparisons */
 
 // comparing a/b and c/d is the same as comparing a*d and b*c, so we
@@ -621,4 +642,42 @@ mod test {
             test(s);
         }
     }
+
+    #[test]
+    fn test_from_float() {
+        fn test<T: Float>(given: T, (numer, denom): (&str, &str)) {
+            let ratio: BigRational = Ratio::from_float(given).unwrap();
+            assert_eq!(ratio, Ratio::new(
+                FromStr::from_str(numer).unwrap(),
+                FromStr::from_str(denom).unwrap()));
+        }
+
+        // f32
+        test(3.14159265359f32, ("13176795", "4194304"));
+        test(2f32.pow(&100.), ("1267650600228229401496703205376", "1"));
+        test(-2f32.pow(&100.), ("-1267650600228229401496703205376", "1"));
+        test(1.0 / 2f32.pow(&100.), ("1", "1267650600228229401496703205376"));
+        test(684729.48391f32, ("1369459", "2"));
+        test(-8573.5918555f32, ("-4389679", "512"));
+
+        // f64
+        test(3.14159265359f64, ("3537118876014453", "1125899906842624"));
+        test(2f64.pow(&100.), ("1267650600228229401496703205376", "1"));
+        test(-2f64.pow(&100.), ("-1267650600228229401496703205376", "1"));
+        test(684729.48391f64, ("367611342500051", "536870912"));
+        test(-8573.5918555, ("-4713381968463931", "549755813888"));
+        test(1.0 / 2f64.pow(&100.), ("1", "1267650600228229401496703205376"));
+    }
+
+    #[test]
+    fn test_from_float_fail() {
+        use std::{f32, f64};
+
+        assert_eq!(Ratio::from_float(f32::NAN), None);
+        assert_eq!(Ratio::from_float(f32::INFINITY), None);
+        assert_eq!(Ratio::from_float(f32::NEG_INFINITY), None);
+        assert_eq!(Ratio::from_float(f64::NAN), None);
+        assert_eq!(Ratio::from_float(f64::INFINITY), None);
+        assert_eq!(Ratio::from_float(f64::NEG_INFINITY), None);
+    }
 }