about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBrendan Zabarauskas <bjzaba@yahoo.com.au>2013-05-07 20:30:51 +1000
committerBrendan Zabarauskas <bjzaba@yahoo.com.au>2013-05-07 20:30:51 +1000
commitcc51186be0c68897042a72bd52147e345ad2a2cd (patch)
treed62915d4465739ce30122cef2e377d8ac7b22230
parenta9ac2b95f485c31e40273dee7f09dcfff8340777 (diff)
downloadrust-cc51186be0c68897042a72bd52147e345ad2a2cd.tar.gz
rust-cc51186be0c68897042a72bd52147e345ad2a2cd.zip
Add is_normal and classify methods to Float trait
-rw-r--r--src/libcore/num/f32.rs55
-rw-r--r--src/libcore/num/f64.rs54
-rw-r--r--src/libcore/num/float.rs59
-rw-r--r--src/libcore/num/num.rs19
4 files changed, 170 insertions, 17 deletions
diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs
index bdd11200419..3c4faa95dd1 100644
--- a/src/libcore/num/f32.rs
+++ b/src/libcore/num/f32.rs
@@ -11,6 +11,7 @@
 //! Operations and constants for `f32`
 
 use num::{Zero, One, strconv};
+use num::{FPCategory, FPNaN, FPInfinite , FPZero, FPSubnormal, FPNormal};
 use prelude::*;
 
 pub use cmath::c_float_targ_consts::*;
@@ -568,12 +569,39 @@ impl Float for f32 {
         *self == Float::infinity() || *self == Float::neg_infinity()
     }
 
-    /// Returns `true` if the number is not infinite or NaN
+    /// Returns `true` if the number is neither infinite or NaN
     #[inline(always)]
     fn is_finite(&self) -> bool {
         !(self.is_NaN() || self.is_infinite())
     }
 
+    /// Returns `true` if the number is neither zero, infinite, subnormal or NaN
+    #[inline(always)]
+    fn is_normal(&self) -> bool {
+        match self.classify() {
+            FPNormal => true,
+            _ => false,
+        }
+    }
+
+    /// Returns the floating point category of the number. If only one property is going to
+    /// be tested, it is generally faster to use the specific predicate instead.
+    fn classify(&self) -> FPCategory {
+        static EXP_MASK: u32 = 0x7f800000;
+        static MAN_MASK: u32 = 0x007fffff;
+
+        match (
+            unsafe { ::cast::transmute::<f32,u32>(*self) } & EXP_MASK,
+            unsafe { ::cast::transmute::<f32,u32>(*self) } & MAN_MASK
+        ) {
+            (EXP_MASK, 0)        => FPInfinite,
+            (EXP_MASK, _)        => FPNaN,
+            (exp, _) if exp != 0 => FPNormal,
+            _ if self.is_zero()  => FPZero,
+            _                    => FPSubnormal,
+        }
+    }
+
     #[inline(always)]
     fn mantissa_digits() -> uint { 24 }
 
@@ -846,6 +874,7 @@ impl num::FromStrRadix for f32 {
 #[cfg(test)]
 mod tests {
     use f32::*;
+    use num::*;
     use super::*;
     use prelude::*;
 
@@ -1041,4 +1070,28 @@ mod tests {
         assert_eq!(Primitive::bits::<f32>(), sys::size_of::<f32>() * 8);
         assert_eq!(Primitive::bytes::<f32>(), sys::size_of::<f32>());
     }
+
+    #[test]
+    fn test_is_normal() {
+        assert!(!Float::NaN::<f32>().is_normal());
+        assert!(!Float::infinity::<f32>().is_normal());
+        assert!(!Float::neg_infinity::<f32>().is_normal());
+        assert!(!Zero::zero::<f32>().is_normal());
+        assert!(!Float::neg_zero::<f32>().is_normal());
+        assert!(1f32.is_normal());
+        assert!(1e-37f32.is_normal());
+        assert!(!1e-38f32.is_normal());
+    }
+
+    #[test]
+    fn test_classify() {
+        assert_eq!(Float::NaN::<f32>().classify(), FPNaN);
+        assert_eq!(Float::infinity::<f32>().classify(), FPInfinite);
+        assert_eq!(Float::neg_infinity::<f32>().classify(), FPInfinite);
+        assert_eq!(Zero::zero::<f32>().classify(), FPZero);
+        assert_eq!(Float::neg_zero::<f32>().classify(), FPZero);
+        assert_eq!(1f32.classify(), FPNormal);
+        assert_eq!(1e-37f32.classify(), FPNormal);
+        assert_eq!(1e-38f32.classify(), FPSubnormal);
+    }
 }
diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs
index 7c6246757cd..30c101fe8a9 100644
--- a/src/libcore/num/f64.rs
+++ b/src/libcore/num/f64.rs
@@ -12,6 +12,7 @@
 
 use libc::c_int;
 use num::{Zero, One, strconv};
+use num::{FPCategory, FPNaN, FPInfinite , FPZero, FPSubnormal, FPNormal};
 use prelude::*;
 
 pub use cmath::c_double_targ_consts::*;
@@ -611,12 +612,39 @@ impl Float for f64 {
         *self == Float::infinity() || *self == Float::neg_infinity()
     }
 
-    /// Returns `true` if the number is not infinite or NaN
+    /// Returns `true` if the number is neither infinite or NaN
     #[inline(always)]
     fn is_finite(&self) -> bool {
         !(self.is_NaN() || self.is_infinite())
     }
 
+    /// Returns `true` if the number is neither zero, infinite, subnormal or NaN
+    #[inline(always)]
+    fn is_normal(&self) -> bool {
+        match self.classify() {
+            FPNormal => true,
+            _ => false,
+        }
+    }
+
+    /// Returns the floating point category of the number. If only one property is going to
+    /// be tested, it is generally faster to use the specific predicate instead.
+    fn classify(&self) -> FPCategory {
+        static EXP_MASK: u64 = 0x7ff0000000000000;
+        static MAN_MASK: u64 = 0x000fffffffffffff;
+
+        match (
+            unsafe { ::cast::transmute::<f64,u64>(*self) } & EXP_MASK,
+            unsafe { ::cast::transmute::<f64,u64>(*self) } & MAN_MASK
+        ) {
+            (EXP_MASK, 0)        => FPInfinite,
+            (EXP_MASK, _)        => FPNaN,
+            (exp, _) if exp != 0 => FPNormal,
+            _ if self.is_zero()  => FPZero,
+            _                    => FPSubnormal,
+        }
+    }
+
     #[inline(always)]
     fn mantissa_digits() -> uint { 53 }
 
@@ -889,6 +917,7 @@ impl num::FromStrRadix for f64 {
 #[cfg(test)]
 mod tests {
     use f64::*;
+    use num::*;
     use super::*;
     use prelude::*;
 
@@ -1088,4 +1117,27 @@ mod tests {
         assert_eq!(Primitive::bits::<f64>(), sys::size_of::<f64>() * 8);
         assert_eq!(Primitive::bytes::<f64>(), sys::size_of::<f64>());
     }
+
+    #[test]
+    fn test_is_normal() {
+        assert!(!Float::NaN::<f64>().is_normal());
+        assert!(!Float::infinity::<f64>().is_normal());
+        assert!(!Float::neg_infinity::<f64>().is_normal());
+        assert!(!Zero::zero::<f64>().is_normal());
+        assert!(!Float::neg_zero::<f64>().is_normal());
+        assert!(1f64.is_normal());
+        assert!(1e-307f64.is_normal());
+        assert!(!1e-308f64.is_normal());
+    }
+
+    #[test]
+    fn test_classify() {
+        assert_eq!(Float::NaN::<f64>().classify(), FPNaN);
+        assert_eq!(Float::infinity::<f64>().classify(), FPInfinite);
+        assert_eq!(Float::neg_infinity::<f64>().classify(), FPInfinite);
+        assert_eq!(Zero::zero::<f64>().classify(), FPZero);
+        assert_eq!(Float::neg_zero::<f64>().classify(), FPZero);
+        assert_eq!(1e-307f64.classify(), FPNormal);
+        assert_eq!(1e-308f64.classify(), FPSubnormal);
+    }
 }
diff --git a/src/libcore/num/float.rs b/src/libcore/num/float.rs
index 35e11309487..9c3d30be0d4 100644
--- a/src/libcore/num/float.rs
+++ b/src/libcore/num/float.rs
@@ -22,6 +22,7 @@
 
 use libc::c_int;
 use num::{Zero, One, strconv};
+use num::FPCategory;
 use prelude::*;
 
 pub use f64::{add, sub, mul, div, rem, lt, le, eq, ne, ge, gt};
@@ -782,32 +783,37 @@ impl Primitive for float {
 
 impl Float for float {
     #[inline(always)]
-    fn NaN() -> float { 0.0 / 0.0 }
+    fn NaN() -> float { Float::NaN::<f64>() as float }
 
     #[inline(always)]
-    fn infinity() -> float { 1.0 / 0.0 }
+    fn infinity() -> float { Float::infinity::<f64>() as float }
 
     #[inline(always)]
-    fn neg_infinity() -> float { -1.0 / 0.0 }
+    fn neg_infinity() -> float { Float::neg_infinity::<f64>() as float }
 
     #[inline(always)]
-    fn neg_zero() -> float { -0.0 }
+    fn neg_zero() -> float { Float::neg_zero::<f64>() as float }
 
     /// Returns `true` if the number is NaN
     #[inline(always)]
-    fn is_NaN(&self) -> bool { *self != *self }
+    fn is_NaN(&self) -> bool { (*self as f64).is_NaN() }
 
     /// Returns `true` if the number is infinite
     #[inline(always)]
-    fn is_infinite(&self) -> bool {
-        *self == Float::infinity() || *self == Float::neg_infinity()
-    }
+    fn is_infinite(&self) -> bool { (*self as f64).is_infinite() }
 
-    /// Returns `true` if the number is not infinite or NaN
+    /// Returns `true` if the number is neither infinite or NaN
     #[inline(always)]
-    fn is_finite(&self) -> bool {
-        !(self.is_NaN() || self.is_infinite())
-    }
+    fn is_finite(&self) -> bool { (*self as f64).is_finite() }
+
+    /// Returns `true` if the number is neither zero, infinite, subnormal or NaN
+    #[inline(always)]
+    fn is_normal(&self) -> bool { (*self as f64).is_normal() }
+
+    /// Returns the floating point category of the number. If only one property is going to
+    /// be tested, it is generally faster to use the specific predicate instead.
+    #[inline(always)]
+    fn classify(&self) -> FPCategory { (*self as f64).classify() }
 
     #[inline(always)]
     fn mantissa_digits() -> uint { Float::mantissa_digits::<f64>() }
@@ -844,9 +850,7 @@ impl Float for float {
     /// than if the operations were performed separately
     ///
     #[inline(always)]
-    fn ln_1p(&self) -> float {
-        (*self as f64).ln_1p() as float
-    }
+    fn ln_1p(&self) -> float { (*self as f64).ln_1p() as float }
 
     ///
     /// Fused multiply-add. Computes `(self * a) + b` with only one rounding error. This
@@ -867,6 +871,7 @@ impl Float for float {
 
 #[cfg(test)]
 mod tests {
+    use num::*;
     use super::*;
     use prelude::*;
 
@@ -1064,6 +1069,30 @@ mod tests {
     }
 
     #[test]
+    fn test_is_normal() {
+        assert!(!Float::NaN::<float>().is_normal());
+        assert!(!Float::infinity::<float>().is_normal());
+        assert!(!Float::neg_infinity::<float>().is_normal());
+        assert!(!Zero::zero::<float>().is_normal());
+        assert!(!Float::neg_zero::<float>().is_normal());
+        assert!(1f.is_normal());
+        assert!(1e-307f.is_normal());
+        assert!(!1e-308f.is_normal());
+    }
+
+    #[test]
+    fn test_classify() {
+        assert_eq!(Float::NaN::<float>().classify(), FPNaN);
+        assert_eq!(Float::infinity::<float>().classify(), FPInfinite);
+        assert_eq!(Float::neg_infinity::<float>().classify(), FPInfinite);
+        assert_eq!(Zero::zero::<float>().classify(), FPZero);
+        assert_eq!(Float::neg_zero::<float>().classify(), FPZero);
+        assert_eq!(1f.classify(), FPNormal);
+        assert_eq!(1e-307f.classify(), FPNormal);
+        assert_eq!(1e-308f.classify(), FPSubnormal);
+    }
+
+    #[test]
     pub fn test_to_str_exact_do_decimal() {
         let s = to_str_exact(5.0, 4u);
         assert_eq!(s, ~"5.0000");
diff --git a/src/libcore/num/num.rs b/src/libcore/num/num.rs
index 7a71729e3e7..50ba55039d4 100644
--- a/src/libcore/num/num.rs
+++ b/src/libcore/num/num.rs
@@ -238,6 +238,23 @@ pub trait Int: Integer
              + BitCount {}
 
 ///
+/// Used for representing the classification of floating point numbers
+///
+#[deriving(Eq)]
+pub enum FPCategory {
+    /// "Not a Number", often obtained by dividing by zero
+    FPNaN,
+    /// Positive or negative infinity
+    FPInfinite ,
+    /// Positive or negative zero
+    FPZero,
+    /// De-normalized floating point representation (less precise than `FPNormal`)
+    FPSubnormal,
+    /// A regular floating point number
+    FPNormal,
+}
+
+///
 /// Primitive floating point numbers
 ///
 pub trait Float: Real
@@ -253,6 +270,8 @@ pub trait Float: Real
     fn is_NaN(&self) -> bool;
     fn is_infinite(&self) -> bool;
     fn is_finite(&self) -> bool;
+    fn is_normal(&self) -> bool;
+    fn classify(&self) -> FPCategory;
 
     fn mantissa_digits() -> uint;
     fn digits() -> uint;