Skip to content

Commit

Permalink
auto merge of #6301 : bjz/rust/numeric-traits, r=pcwalton
Browse files Browse the repository at this point in the history
This is part of the redesign of the numeric traits tracked in issue #4819.

Renamed:

- `Exponential::expm1` -> `Float::exp_m1` - for consistency with underscore usage elsewhere
- `Exponential::log` -> `Exponential::ln` - a less ambiguous name for the natural logarithm
- `{float, f64, f32}::logarithm` -> `Exponential::log` - for arbitrary base logarithms
- `Real::log_2` -> `Real::ln_2`  - for consistency with `ln`
- `Real::log_10` -> `Real::ln_10` - for consistency with `ln`

Added:

- `Signed::abs_sub` - wraps libm's `fdim` function
- `Float::is_normal` - returns `true` if the number is neither zero, infinite, subnormal or NaN
- `Float::classify` - returns the floating point category of the number
- `Float::ln_1p` - returns the natural logarithm of the number plus one
  • Loading branch information
bors committed May 7, 2013
2 parents 3a34e93 + cc51186 commit 847552f
Show file tree
Hide file tree
Showing 7 changed files with 422 additions and 76 deletions.
9 changes: 5 additions & 4 deletions src/libcore/num/cmath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ pub mod c_double_utils {
unsafe fn erf(n: c_double) -> c_double;
unsafe fn erfc(n: c_double) -> c_double;
unsafe fn exp(n: c_double) -> c_double;
unsafe fn expm1(n: c_double) -> c_double;
// rename: for consistency with underscore usage elsewhere
#[link_name="expm1"] unsafe fn exp_m1(n: c_double) -> c_double;
unsafe fn exp2(n: c_double) -> c_double;
#[link_name="fabs"] unsafe fn abs(n: c_double) -> c_double;
// rename: for clarity and consistency with add/sub/mul/div
Expand Down Expand Up @@ -63,7 +64,7 @@ pub mod c_double_utils {
// renamed: "logb" /often/ is confused for log2 by beginners
#[link_name="logb"] unsafe fn log_radix(n: c_double) -> c_double;
// renamed: to be consitent with log as ln
#[link_name="log1p"] unsafe fn ln1p(n: c_double) -> c_double;
#[link_name="log1p"] unsafe fn ln_1p(n: c_double) -> c_double;
unsafe fn log10(n: c_double) -> c_double;
unsafe fn log2(n: c_double) -> c_double;
#[link_name="ilogb"] unsafe fn ilog_radix(n: c_double) -> c_int;
Expand Down Expand Up @@ -117,7 +118,7 @@ pub mod c_float_utils {
#[link_name="erff"] unsafe fn erf(n: c_float) -> c_float;
#[link_name="erfcf"] unsafe fn erfc(n: c_float) -> c_float;
#[link_name="expf"] unsafe fn exp(n: c_float) -> c_float;
#[link_name="expm1f"]unsafe fn expm1(n: c_float) -> c_float;
#[link_name="expm1f"]unsafe fn exp_m1(n: c_float) -> c_float;
#[link_name="exp2f"] unsafe fn exp2(n: c_float) -> c_float;
#[link_name="fabsf"] unsafe fn abs(n: c_float) -> c_float;
#[link_name="fdimf"]
Expand Down Expand Up @@ -148,7 +149,7 @@ pub mod c_float_utils {

#[link_name="logf"] unsafe fn ln(n: c_float) -> c_float;
#[link_name="logbf"] unsafe fn log_radix(n: c_float) -> c_float;
#[link_name="log1pf"] unsafe fn ln1p(n: c_float) -> c_float;
#[link_name="log1pf"] unsafe fn ln_1p(n: c_float) -> c_float;
#[link_name="log2f"] unsafe fn log2(n: c_float) -> c_float;
#[link_name="log10f"] unsafe fn log10(n: c_float) -> c_float;
#[link_name="ilogbf"] unsafe fn ilog_radix(n: c_float) -> c_int;
Expand Down
147 changes: 123 additions & 24 deletions 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 @@ -82,7 +83,7 @@ delegate!(
fn cosh(n: c_float) -> c_float = c_float_utils::cosh,
fn erf(n: c_float) -> c_float = c_float_utils::erf,
fn erfc(n: c_float) -> c_float = c_float_utils::erfc,
fn expm1(n: c_float) -> c_float = c_float_utils::expm1,
fn exp_m1(n: c_float) -> c_float = c_float_utils::exp_m1,
fn abs_sub(a: c_float, b: c_float) -> c_float = c_float_utils::abs_sub,
fn fmax(a: c_float, b: c_float) -> c_float = c_float_utils::fmax,
fn fmin(a: c_float, b: c_float) -> c_float = c_float_utils::fmin,
Expand All @@ -92,7 +93,7 @@ delegate!(
fn ldexp(x: c_float, n: c_int) -> c_float = c_float_utils::ldexp,
fn lgamma(n: c_float, sign: &mut c_int) -> c_float = c_float_utils::lgamma,
fn log_radix(n: c_float) -> c_float = c_float_utils::log_radix,
fn ln1p(n: c_float) -> c_float = c_float_utils::ln1p,
fn ln_1p(n: c_float) -> c_float = c_float_utils::ln_1p,
fn ilog_radix(n: c_float) -> c_int = c_float_utils::ilog_radix,
fn modf(n: c_float, iptr: &mut c_float) -> c_float = c_float_utils::modf,
fn round(n: c_float) -> c_float = c_float_utils::round,
Expand Down Expand Up @@ -195,11 +196,6 @@ pub mod consts {
pub static ln_10: f32 = 2.30258509299404568401799145468436421_f32;
}

#[inline(always)]
pub fn logarithm(n: f32, b: f32) -> f32 {
return log2(n) / log2(b);
}

impl Num for f32 {}

#[cfg(notest)]
Expand Down Expand Up @@ -317,6 +313,13 @@ impl Signed for f32 {
#[inline(always)]
fn abs(&self) -> f32 { abs(*self) }

///
/// The positive difference of two numbers. Returns `0.0` if the number is less than or
/// equal to `other`, otherwise the difference between`self` and `other` is returned.
///
#[inline(always)]
fn abs_sub(&self, other: &f32) -> f32 { abs_sub(*self, *other) }

///
/// # Returns
///
Expand Down Expand Up @@ -413,21 +416,27 @@ impl Trigonometric for f32 {
}

impl Exponential for f32 {
/// Returns the exponential of the number
#[inline(always)]
fn exp(&self) -> f32 { exp(*self) }

/// Returns 2 raised to the power of the number
#[inline(always)]
fn exp2(&self) -> f32 { exp2(*self) }

/// Returns the natural logarithm of the number
#[inline(always)]
fn expm1(&self) -> f32 { expm1(*self) }
fn ln(&self) -> f32 { ln(*self) }

/// Returns the logarithm of the number with respect to an arbitrary base
#[inline(always)]
fn log(&self) -> f32 { ln(*self) }
fn log(&self, base: f32) -> f32 { self.ln() / base.ln() }

/// Returns the base 2 logarithm of the number
#[inline(always)]
fn log2(&self) -> f32 { log2(*self) }

/// Returns the base 10 logarithm of the number
#[inline(always)]
fn log10(&self) -> f32 { log10(*self) }
}
Expand Down Expand Up @@ -504,13 +513,13 @@ impl Real for f32 {
#[inline(always)]
fn log10_e() -> f32 { 0.434294481903251827651128918916605082 }

/// log(2.0)
/// ln(2.0)
#[inline(always)]
fn log_2() -> f32 { 0.693147180559945309417232121458176568 }
fn ln_2() -> f32 { 0.693147180559945309417232121458176568 }

/// log(10.0)
/// ln(10.0)
#[inline(always)]
fn log_10() -> f32 { 2.30258509299404568401799145468436421 }
fn ln_10() -> f32 { 2.30258509299404568401799145468436421 }

/// Converts to degrees, assuming the number is in radians
#[inline(always)]
Expand Down Expand Up @@ -550,9 +559,49 @@ impl Float for f32 {
#[inline(always)]
fn neg_zero() -> f32 { -0.0 }

/// Returns `true` if the number is NaN
#[inline(always)]
fn is_NaN(&self) -> bool { *self != *self }

/// Returns `true` if the number is infinite
#[inline(always)]
fn is_infinite(&self) -> bool {
*self == Float::infinity() || *self == Float::neg_infinity()
}

/// 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 All @@ -574,17 +623,19 @@ impl Float for f32 {
#[inline(always)]
fn max_10_exp() -> int { 38 }

/// Returns `true` if the number is infinite
///
/// Returns the exponential of the number, minus `1`, in a way that is accurate
/// even if the number is close to zero
///
#[inline(always)]
fn is_infinite(&self) -> bool {
*self == Float::infinity() || *self == Float::neg_infinity()
}
fn exp_m1(&self) -> f32 { exp_m1(*self) }

/// Returns `true` if the number is finite
///
/// Returns the natural logarithm of the number plus `1` (`ln(1+n)`) more accurately
/// than if the operations were performed separately
///
#[inline(always)]
fn is_finite(&self) -> bool {
!(self.is_NaN() || self.is_infinite())
}
fn ln_1p(&self) -> f32 { ln_1p(*self) }

///
/// Fused multiply-add. Computes `(self * a) + b` with only one rounding error. This
Expand Down Expand Up @@ -823,6 +874,7 @@ impl num::FromStrRadix for f32 {
#[cfg(test)]
mod tests {
use f32::*;
use num::*;
use super::*;
use prelude::*;

Expand Down Expand Up @@ -938,12 +990,12 @@ mod tests {
assert_approx_eq!(Real::frac_1_sqrt2::<f32>(), 1f32 / 2f32.sqrt());
assert_approx_eq!(Real::log2_e::<f32>(), Real::e::<f32>().log2());
assert_approx_eq!(Real::log10_e::<f32>(), Real::e::<f32>().log10());
assert_approx_eq!(Real::log_2::<f32>(), 2f32.log());
assert_approx_eq!(Real::log_10::<f32>(), 10f32.log());
assert_approx_eq!(Real::ln_2::<f32>(), 2f32.ln());
assert_approx_eq!(Real::ln_10::<f32>(), 10f32.ln());
}

#[test]
pub fn test_signed() {
pub fn test_abs() {
assert_eq!(infinity.abs(), infinity);
assert_eq!(1f32.abs(), 1f32);
assert_eq!(0f32.abs(), 0f32);
Expand All @@ -952,7 +1004,24 @@ mod tests {
assert_eq!(neg_infinity.abs(), infinity);
assert_eq!((1f32/neg_infinity).abs(), 0f32);
assert!(NaN.abs().is_NaN());
}

#[test]
fn test_abs_sub() {
assert_eq!((-1f32).abs_sub(&1f32), 0f32);
assert_eq!(1f32.abs_sub(&1f32), 0f32);
assert_eq!(1f32.abs_sub(&0f32), 1f32);
assert_eq!(1f32.abs_sub(&-1f32), 2f32);
assert_eq!(neg_infinity.abs_sub(&0f32), 0f32);
assert_eq!(infinity.abs_sub(&1f32), infinity);
assert_eq!(0f32.abs_sub(&neg_infinity), infinity);
assert_eq!(0f32.abs_sub(&infinity), 0f32);
assert!(NaN.abs_sub(&-1f32).is_NaN());
assert!(1f32.abs_sub(&NaN).is_NaN());
}

#[test]
fn test_signum() {
assert_eq!(infinity.signum(), 1f32);
assert_eq!(1f32.signum(), 1f32);
assert_eq!(0f32.signum(), 1f32);
Expand All @@ -961,7 +1030,10 @@ mod tests {
assert_eq!(neg_infinity.signum(), -1f32);
assert_eq!((1f32/neg_infinity).signum(), -1f32);
assert!(NaN.signum().is_NaN());
}

#[test]
fn test_is_positive() {
assert!(infinity.is_positive());
assert!(1f32.is_positive());
assert!(0f32.is_positive());
Expand All @@ -970,7 +1042,10 @@ mod tests {
assert!(!neg_infinity.is_positive());
assert!(!(1f32/neg_infinity).is_positive());
assert!(!NaN.is_positive());
}

#[test]
fn test_is_negative() {
assert!(!infinity.is_negative());
assert!(!1f32.is_negative());
assert!(!0f32.is_negative());
Expand All @@ -995,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);
}
}
Loading

0 comments on commit 847552f

Please sign in to comment.