diff options
| author | Brendan Zabarauskas <bjzaba@yahoo.com.au> | 2013-05-07 20:30:51 +1000 |
|---|---|---|
| committer | Brendan Zabarauskas <bjzaba@yahoo.com.au> | 2013-05-07 20:30:51 +1000 |
| commit | cc51186be0c68897042a72bd52147e345ad2a2cd (patch) | |
| tree | d62915d4465739ce30122cef2e377d8ac7b22230 | |
| parent | a9ac2b95f485c31e40273dee7f09dcfff8340777 (diff) | |
| download | rust-cc51186be0c68897042a72bd52147e345ad2a2cd.tar.gz rust-cc51186be0c68897042a72bd52147e345ad2a2cd.zip | |
Add is_normal and classify methods to Float trait
| -rw-r--r-- | src/libcore/num/f32.rs | 55 | ||||
| -rw-r--r-- | src/libcore/num/f64.rs | 54 | ||||
| -rw-r--r-- | src/libcore/num/float.rs | 59 | ||||
| -rw-r--r-- | src/libcore/num/num.rs | 19 |
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; |
