Skip to content

Commit

Permalink
Add is_normal and classify methods to Float trait
Browse files Browse the repository at this point in the history
  • Loading branch information
brendanzab committed May 7, 2013
1 parent a9ac2b9 commit cc51186
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 17 deletions.
55 changes: 54 additions & 1 deletion src/libcore/num/f32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -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 }

Expand Down Expand Up @@ -846,6 +874,7 @@ impl num::FromStrRadix for f32 {
#[cfg(test)]
mod tests {
use f32::*;
use num::*;
use super::*;
use prelude::*;

Expand Down Expand Up @@ -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);
}
}
54 changes: 53 additions & 1 deletion src/libcore/num/f64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -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 }

Expand Down Expand Up @@ -889,6 +917,7 @@ impl num::FromStrRadix for f64 {
#[cfg(test)]
mod tests {
use f64::*;
use num::*;
use super::*;
use prelude::*;

Expand Down Expand Up @@ -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);
}
}
59 changes: 44 additions & 15 deletions src/libcore/num/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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>() }
Expand Down Expand Up @@ -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
Expand All @@ -867,6 +871,7 @@ impl Float for float {

#[cfg(test)]
mod tests {
use num::*;
use super::*;
use prelude::*;

Expand Down Expand Up @@ -1063,6 +1068,30 @@ mod tests {
assert_eq!(Primitive::bytes::<float>(), sys::size_of::<float>());
}

#[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);
Expand Down
19 changes: 19 additions & 0 deletions src/libcore/num/num.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,23 @@ pub trait Int: Integer
+ Bitwise
+ 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
///
Expand All @@ -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;
Expand Down

5 comments on commit cc51186

@bors
Copy link
Contributor

@bors bors commented on cc51186 May 7, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saw approval from pcwalton
at brendanzab@cc51186

@bors
Copy link
Contributor

@bors bors commented on cc51186 May 7, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merging bjz/rust/numeric-traits = cc51186 into auto

@bors
Copy link
Contributor

@bors bors commented on cc51186 May 7, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bjz/rust/numeric-traits = cc51186 merged ok, testing candidate = 847552f

@bors
Copy link
Contributor

@bors bors commented on cc51186 May 8, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bors
Copy link
Contributor

@bors bors commented on cc51186 May 8, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast-forwarding incoming to auto = 847552f

Please sign in to comment.