diff --git a/src/libcore/num/cmath.rs b/src/libcore/num/cmath.rs index 8a0a88235d20c..a80703fafa3d2 100644 --- a/src/libcore/num/cmath.rs +++ b/src/libcore/num/cmath.rs @@ -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 @@ -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; @@ -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"] @@ -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; diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index 7c13f136a80f2..3c4faa95dd190 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::*; @@ -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, @@ -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, @@ -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)] @@ -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 /// @@ -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) } } @@ -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)] @@ -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::(*self) } & EXP_MASK, + unsafe { ::cast::transmute::(*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 } @@ -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 @@ -823,6 +874,7 @@ impl num::FromStrRadix for f32 { #[cfg(test)] mod tests { use f32::*; + use num::*; use super::*; use prelude::*; @@ -938,12 +990,12 @@ mod tests { assert_approx_eq!(Real::frac_1_sqrt2::(), 1f32 / 2f32.sqrt()); assert_approx_eq!(Real::log2_e::(), Real::e::().log2()); assert_approx_eq!(Real::log10_e::(), Real::e::().log10()); - assert_approx_eq!(Real::log_2::(), 2f32.log()); - assert_approx_eq!(Real::log_10::(), 10f32.log()); + assert_approx_eq!(Real::ln_2::(), 2f32.ln()); + assert_approx_eq!(Real::ln_10::(), 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); @@ -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); @@ -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()); @@ -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()); @@ -995,4 +1070,28 @@ mod tests { assert_eq!(Primitive::bits::(), sys::size_of::() * 8); assert_eq!(Primitive::bytes::(), sys::size_of::()); } + + #[test] + fn test_is_normal() { + assert!(!Float::NaN::().is_normal()); + assert!(!Float::infinity::().is_normal()); + assert!(!Float::neg_infinity::().is_normal()); + assert!(!Zero::zero::().is_normal()); + assert!(!Float::neg_zero::().is_normal()); + assert!(1f32.is_normal()); + assert!(1e-37f32.is_normal()); + assert!(!1e-38f32.is_normal()); + } + + #[test] + fn test_classify() { + assert_eq!(Float::NaN::().classify(), FPNaN); + assert_eq!(Float::infinity::().classify(), FPInfinite); + assert_eq!(Float::neg_infinity::().classify(), FPInfinite); + assert_eq!(Zero::zero::().classify(), FPZero); + assert_eq!(Float::neg_zero::().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 e5f10c23ecd87..30c101fe8a9c6 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::*; @@ -84,7 +85,7 @@ delegate!( fn cosh(n: c_double) -> c_double = c_double_utils::cosh, fn erf(n: c_double) -> c_double = c_double_utils::erf, fn erfc(n: c_double) -> c_double = c_double_utils::erfc, - fn expm1(n: c_double) -> c_double = c_double_utils::expm1, + fn exp_m1(n: c_double) -> c_double = c_double_utils::exp_m1, fn abs_sub(a: c_double, b: c_double) -> c_double = c_double_utils::abs_sub, fn fmax(a: c_double, b: c_double) -> c_double = c_double_utils::fmax, fn fmin(a: c_double, b: c_double) -> c_double = c_double_utils::fmin, @@ -94,7 +95,7 @@ delegate!( fn ldexp(x: c_double, n: c_int) -> c_double = c_double_utils::ldexp, fn lgamma(n: c_double, sign: &mut c_int) -> c_double = c_double_utils::lgamma, fn log_radix(n: c_double) -> c_double = c_double_utils::log_radix, - fn ln1p(n: c_double) -> c_double = c_double_utils::ln1p, + fn ln_1p(n: c_double) -> c_double = c_double_utils::ln_1p, fn ilog_radix(n: c_double) -> c_int = c_double_utils::ilog_radix, fn modf(n: c_double, iptr: &mut c_double) -> c_double = c_double_utils::modf, fn round(n: c_double) -> c_double = c_double_utils::round, @@ -218,11 +219,6 @@ pub mod consts { pub static ln_10: f64 = 2.30258509299404568401799145468436421_f64; } -#[inline(always)] -pub fn logarithm(n: f64, b: f64) -> f64 { - return log2(n) / log2(b); -} - impl Num for f64 {} #[cfg(notest)] @@ -330,6 +326,13 @@ impl Signed for f64 { #[inline(always)] fn abs(&self) -> f64 { 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: &f64) -> f64 { abs_sub(*self, *other) } + /// /// # Returns /// @@ -426,21 +429,27 @@ impl Trigonometric for f64 { } impl Exponential for f64 { + /// Returns the exponential of the number #[inline(always)] fn exp(&self) -> f64 { exp(*self) } + /// Returns 2 raised to the power of the number #[inline(always)] fn exp2(&self) -> f64 { exp2(*self) } + /// Returns the natural logarithm of the number #[inline(always)] - fn expm1(&self) -> f64 { expm1(*self) } + fn ln(&self) -> f64 { ln(*self) } + /// Returns the logarithm of the number with respect to an arbitrary base #[inline(always)] - fn log(&self) -> f64 { ln(*self) } + fn log(&self, base: f64) -> f64 { self.ln() / base.ln() } + /// Returns the base 2 logarithm of the number #[inline(always)] fn log2(&self) -> f64 { log2(*self) } + /// Returns the base 10 logarithm of the number #[inline(always)] fn log10(&self) -> f64 { log10(*self) } } @@ -517,13 +526,13 @@ impl Real for f64 { #[inline(always)] fn log10_e() -> f64 { 0.434294481903251827651128918916605082 } - /// log(2.0) + /// ln(2.0) #[inline(always)] - fn log_2() -> f64 { 0.693147180559945309417232121458176568 } + fn ln_2() -> f64 { 0.693147180559945309417232121458176568 } - /// log(10.0) + /// ln(10.0) #[inline(always)] - fn log_10() -> f64 { 2.30258509299404568401799145468436421 } + fn ln_10() -> f64 { 2.30258509299404568401799145468436421 } /// Converts to degrees, assuming the number is in radians #[inline(always)] @@ -593,6 +602,7 @@ impl Float for f64 { #[inline(always)] fn neg_zero() -> f64 { -0.0 } + /// Returns `true` if the number is NaN #[inline(always)] fn is_NaN(&self) -> bool { *self != *self } @@ -602,12 +612,39 @@ impl Float for f64 { *self == Float::infinity() || *self == Float::neg_infinity() } - /// Returns `true` if the number is finite + /// 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::(*self) } & EXP_MASK, + unsafe { ::cast::transmute::(*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 } @@ -629,6 +666,20 @@ impl Float for f64 { #[inline(always)] fn max_10_exp() -> int { 308 } + /// + /// 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 exp_m1(&self) -> f64 { exp_m1(*self) } + + /// + /// Returns the natural logarithm of the number plus `1` (`ln(1+n)`) more accurately + /// than if the operations were performed separately + /// + #[inline(always)] + fn ln_1p(&self) -> f64 { ln_1p(*self) } + /// /// Fused multiply-add. Computes `(self * a) + b` with only one rounding error. This /// produces a more accurate result with better performance than a separate multiplication @@ -866,6 +917,7 @@ impl num::FromStrRadix for f64 { #[cfg(test)] mod tests { use f64::*; + use num::*; use super::*; use prelude::*; @@ -985,12 +1037,12 @@ mod tests { assert_approx_eq!(Real::frac_1_sqrt2::(), 1f64 / 2f64.sqrt()); assert_approx_eq!(Real::log2_e::(), Real::e::().log2()); assert_approx_eq!(Real::log10_e::(), Real::e::().log10()); - assert_approx_eq!(Real::log_2::(), 2f64.log()); - assert_approx_eq!(Real::log_10::(), 10f64.log()); + assert_approx_eq!(Real::ln_2::(), 2f64.ln()); + assert_approx_eq!(Real::ln_10::(), 10f64.ln()); } #[test] - pub fn test_signed() { + pub fn test_abs() { assert_eq!(infinity.abs(), infinity); assert_eq!(1f64.abs(), 1f64); assert_eq!(0f64.abs(), 0f64); @@ -999,7 +1051,24 @@ mod tests { assert_eq!(neg_infinity.abs(), infinity); assert_eq!((1f64/neg_infinity).abs(), 0f64); assert!(NaN.abs().is_NaN()); + } + #[test] + fn test_abs_sub() { + assert_eq!((-1f64).abs_sub(&1f64), 0f64); + assert_eq!(1f64.abs_sub(&1f64), 0f64); + assert_eq!(1f64.abs_sub(&0f64), 1f64); + assert_eq!(1f64.abs_sub(&-1f64), 2f64); + assert_eq!(neg_infinity.abs_sub(&0f64), 0f64); + assert_eq!(infinity.abs_sub(&1f64), infinity); + assert_eq!(0f64.abs_sub(&neg_infinity), infinity); + assert_eq!(0f64.abs_sub(&infinity), 0f64); + assert!(NaN.abs_sub(&-1f64).is_NaN()); + assert!(1f64.abs_sub(&NaN).is_NaN()); + } + + #[test] + fn test_signum() { assert_eq!(infinity.signum(), 1f64); assert_eq!(1f64.signum(), 1f64); assert_eq!(0f64.signum(), 1f64); @@ -1008,7 +1077,10 @@ mod tests { assert_eq!(neg_infinity.signum(), -1f64); assert_eq!((1f64/neg_infinity).signum(), -1f64); assert!(NaN.signum().is_NaN()); + } + #[test] + fn test_is_positive() { assert!(infinity.is_positive()); assert!(1f64.is_positive()); assert!(0f64.is_positive()); @@ -1017,7 +1089,10 @@ mod tests { assert!(!neg_infinity.is_positive()); assert!(!(1f64/neg_infinity).is_positive()); assert!(!NaN.is_positive()); + } + #[test] + fn test_is_negative() { assert!(!infinity.is_negative()); assert!(!1f64.is_negative()); assert!(!0f64.is_negative()); @@ -1042,4 +1117,27 @@ mod tests { assert_eq!(Primitive::bits::(), sys::size_of::() * 8); assert_eq!(Primitive::bytes::(), sys::size_of::()); } + + #[test] + fn test_is_normal() { + assert!(!Float::NaN::().is_normal()); + assert!(!Float::infinity::().is_normal()); + assert!(!Float::neg_infinity::().is_normal()); + assert!(!Zero::zero::().is_normal()); + assert!(!Float::neg_zero::().is_normal()); + assert!(1f64.is_normal()); + assert!(1e-307f64.is_normal()); + assert!(!1e-308f64.is_normal()); + } + + #[test] + fn test_classify() { + assert_eq!(Float::NaN::().classify(), FPNaN); + assert_eq!(Float::infinity::().classify(), FPInfinite); + assert_eq!(Float::neg_infinity::().classify(), FPInfinite); + assert_eq!(Zero::zero::().classify(), FPZero); + assert_eq!(Float::neg_zero::().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 a548165326396..9c3d30be0d499 100644 --- a/src/libcore/num/float.rs +++ b/src/libcore/num/float.rs @@ -22,14 +22,14 @@ 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}; -pub use f64::logarithm; pub use f64::{acos, asin, atan2, cbrt, ceil, copysign, cosh, floor}; -pub use f64::{erf, erfc, exp, expm1, exp2, abs_sub}; +pub use f64::{erf, erfc, exp, exp_m1, exp2, abs_sub}; pub use f64::{mul_add, fmax, fmin, next_after, frexp, hypot, ldexp}; -pub use f64::{lgamma, ln, log_radix, ln1p, log10, log2, ilog_radix}; +pub use f64::{lgamma, ln, log_radix, ln_1p, log10, log2, ilog_radix}; pub use f64::{modf, pow, powi, round, sinh, tanh, tgamma, trunc}; pub use f64::{j0, j1, jn, y0, y1, yn}; @@ -533,31 +533,37 @@ impl Trigonometric for float { } impl Exponential for float { + /// Returns the exponential of the number #[inline(always)] fn exp(&self) -> float { (*self as f64).exp() as float } + /// Returns 2 raised to the power of the number #[inline(always)] fn exp2(&self) -> float { (*self as f64).exp2() as float } + /// Returns the natural logarithm of the number #[inline(always)] - fn expm1(&self) -> float { - (*self as f64).expm1() as float + fn ln(&self) -> float { + (*self as f64).ln() as float } + /// Returns the logarithm of the number with respect to an arbitrary base #[inline(always)] - fn log(&self) -> float { - (*self as f64).log() as float + fn log(&self, base: float) -> float { + (*self as f64).log(base as f64) as float } + /// Returns the base 2 logarithm of the number #[inline(always)] fn log2(&self) -> float { (*self as f64).log2() as float } + /// Returns the base 10 logarithm of the number #[inline(always)] fn log10(&self) -> float { (*self as f64).log10() as float @@ -642,13 +648,13 @@ impl Real for float { #[inline(always)] fn log10_e() -> float { 0.434294481903251827651128918916605082 } - /// log(2.0) + /// ln(2.0) #[inline(always)] - fn log_2() -> float { 0.693147180559945309417232121458176568 } + fn ln_2() -> float { 0.693147180559945309417232121458176568 } - /// log(10.0) + /// ln(10.0) #[inline(always)] - fn log_10() -> float { 2.30258509299404568401799145468436421 } + fn ln_10() -> float { 2.30258509299404568401799145468436421 } /// Converts to degrees, assuming the number is in radians #[inline(always)] @@ -729,6 +735,15 @@ impl Signed for float { #[inline(always)] fn abs(&self) -> float { 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: &float) -> float { + (*self as f64).abs_sub(&(*other as f64)) as float + } + /// /// # Returns /// @@ -768,19 +783,37 @@ impl Primitive for float { impl Float for float { #[inline(always)] - fn NaN() -> float { 0.0 / 0.0 } + fn NaN() -> float { Float::NaN::() as float } #[inline(always)] - fn infinity() -> float { 1.0 / 0.0 } + fn infinity() -> float { Float::infinity::() as float } #[inline(always)] - fn neg_infinity() -> float { -1.0 / 0.0 } + fn neg_infinity() -> float { Float::neg_infinity::() as float } #[inline(always)] - fn neg_zero() -> float { -0.0 } + fn neg_zero() -> float { Float::neg_zero::() as float } + /// Returns `true` if the number is NaN + #[inline(always)] + fn is_NaN(&self) -> bool { (*self as f64).is_NaN() } + + /// Returns `true` if the number is infinite #[inline(always)] - fn is_NaN(&self) -> bool { *self != *self } + fn is_infinite(&self) -> bool { (*self as f64).is_infinite() } + + /// Returns `true` if the number is neither infinite or NaN + #[inline(always)] + 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::() } @@ -803,17 +836,21 @@ impl Float for float { #[inline(always)] fn max_10_exp() -> int { Float::max_10_exp::() } - /// 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) -> float { + (*self as f64).exp_m1() as float } - /// 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) -> float { (*self as f64).ln_1p() as float } /// /// Fused multiply-add. Computes `(self * a) + b` with only one rounding error. This @@ -834,6 +871,7 @@ impl Float for float { #[cfg(test)] mod tests { + use num::*; use super::*; use prelude::*; @@ -949,12 +987,12 @@ mod tests { assert_approx_eq!(Real::frac_1_sqrt2::(), 1f / 2f.sqrt()); assert_approx_eq!(Real::log2_e::(), Real::e::().log2()); assert_approx_eq!(Real::log10_e::(), Real::e::().log10()); - assert_approx_eq!(Real::log_2::(), 2f.log()); - assert_approx_eq!(Real::log_10::(), 10f.log()); + assert_approx_eq!(Real::ln_2::(), 2f.ln()); + assert_approx_eq!(Real::ln_10::(), 10f.ln()); } #[test] - fn test_signed() { + fn test_abs() { assert_eq!(infinity.abs(), infinity); assert_eq!(1f.abs(), 1f); assert_eq!(0f.abs(), 0f); @@ -963,7 +1001,24 @@ mod tests { assert_eq!(neg_infinity.abs(), infinity); assert_eq!((1f/neg_infinity).abs(), 0f); assert!(NaN.abs().is_NaN()); + } + #[test] + fn test_abs_sub() { + assert_eq!((-1f).abs_sub(&1f), 0f); + assert_eq!(1f.abs_sub(&1f), 0f); + assert_eq!(1f.abs_sub(&0f), 1f); + assert_eq!(1f.abs_sub(&-1f), 2f); + assert_eq!(neg_infinity.abs_sub(&0f), 0f); + assert_eq!(infinity.abs_sub(&1f), infinity); + assert_eq!(0f.abs_sub(&neg_infinity), infinity); + assert_eq!(0f.abs_sub(&infinity), 0f); + assert!(NaN.abs_sub(&-1f).is_NaN()); + assert!(1f.abs_sub(&NaN).is_NaN()); + } + + #[test] + fn test_signum() { assert_eq!(infinity.signum(), 1f); assert_eq!(1f.signum(), 1f); assert_eq!(0f.signum(), 1f); @@ -972,7 +1027,10 @@ mod tests { assert_eq!(neg_infinity.signum(), -1f); assert_eq!((1f/neg_infinity).signum(), -1f); assert!(NaN.signum().is_NaN()); + } + #[test] + fn test_is_positive() { assert!(infinity.is_positive()); assert!(1f.is_positive()); assert!(0f.is_positive()); @@ -981,7 +1039,10 @@ mod tests { assert!(!neg_infinity.is_positive()); assert!(!(1f/neg_infinity).is_positive()); assert!(!NaN.is_positive()); + } + #[test] + fn test_is_negative() { assert!(!infinity.is_negative()); assert!(!1f.is_negative()); assert!(!0f.is_negative()); @@ -1007,6 +1068,30 @@ mod tests { assert_eq!(Primitive::bytes::(), sys::size_of::()); } + #[test] + fn test_is_normal() { + assert!(!Float::NaN::().is_normal()); + assert!(!Float::infinity::().is_normal()); + assert!(!Float::neg_infinity::().is_normal()); + assert!(!Zero::zero::().is_normal()); + assert!(!Float::neg_zero::().is_normal()); + assert!(1f.is_normal()); + assert!(1e-307f.is_normal()); + assert!(!1e-308f.is_normal()); + } + + #[test] + fn test_classify() { + assert_eq!(Float::NaN::().classify(), FPNaN); + assert_eq!(Float::infinity::().classify(), FPInfinite); + assert_eq!(Float::neg_infinity::().classify(), FPInfinite); + assert_eq!(Zero::zero::().classify(), FPZero); + assert_eq!(Float::neg_zero::().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); diff --git a/src/libcore/num/int-template.rs b/src/libcore/num/int-template.rs index 95c187a7be22e..06a9a0b45627d 100644 --- a/src/libcore/num/int-template.rs +++ b/src/libcore/num/int-template.rs @@ -264,6 +264,15 @@ impl Signed for T { if self.is_negative() { -*self } else { *self } } + /// + /// The positive difference of two numbers. Returns `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: &T) -> T { + if *self <= *other { 0 } else { *self - *other } + } + /// /// # Returns /// @@ -554,21 +563,38 @@ mod tests { } #[test] - pub fn test_signed() { + pub fn test_abs() { assert_eq!((1 as T).abs(), 1 as T); assert_eq!((0 as T).abs(), 0 as T); assert_eq!((-1 as T).abs(), 1 as T); + } + + #[test] + fn test_abs_sub() { + assert_eq!((-1 as T).abs_sub(&(1 as T)), 0 as T); + assert_eq!((1 as T).abs_sub(&(1 as T)), 0 as T); + assert_eq!((1 as T).abs_sub(&(0 as T)), 1 as T); + assert_eq!((1 as T).abs_sub(&(-1 as T)), 2 as T); + } + #[test] + fn test_signum() { assert_eq!((1 as T).signum(), 1 as T); assert_eq!((0 as T).signum(), 0 as T); assert_eq!((-0 as T).signum(), 0 as T); assert_eq!((-1 as T).signum(), -1 as T); + } + #[test] + fn test_is_positive() { assert!((1 as T).is_positive()); assert!(!(0 as T).is_positive()); assert!(!(-0 as T).is_positive()); assert!(!(-1 as T).is_positive()); + } + #[test] + fn test_is_negative() { assert!(!(1 as T).is_negative()); assert!(!(0 as T).is_negative()); assert!(!(-0 as T).is_negative()); diff --git a/src/libcore/num/num.rs b/src/libcore/num/num.rs index caa14ea802f6c..50ba55039d408 100644 --- a/src/libcore/num/num.rs +++ b/src/libcore/num/num.rs @@ -55,7 +55,9 @@ pub trait One { pub trait Signed: Num + Neg { fn abs(&self) -> Self; + fn abs_sub(&self, other: &Self) -> Self; fn signum(&self) -> Self; + fn is_positive(&self) -> bool; fn is_negative(&self) -> bool; } @@ -121,8 +123,8 @@ pub trait Trigonometric { pub trait Exponential { fn exp(&self) -> Self; fn exp2(&self) -> Self; - fn expm1(&self) -> Self; - fn log(&self) -> Self; + fn ln(&self) -> Self; + fn log(&self, base: Self) -> Self; fn log2(&self) -> Self; fn log10(&self) -> Self; } @@ -158,8 +160,8 @@ pub trait Real: Signed fn e() -> Self; fn log2_e() -> Self; fn log10_e() -> Self; - fn log_2() -> Self; - fn log_10() -> Self; + fn ln_2() -> Self; + fn ln_10() -> Self; // Angular conversions fn to_degrees(&self) -> Self; @@ -235,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 /// @@ -251,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; @@ -260,6 +281,8 @@ pub trait Float: Real fn min_10_exp() -> int; fn max_10_exp() -> int; + fn exp_m1(&self) -> Self; + fn ln_1p(&self) -> Self; fn mul_add(&self, a: Self, b: Self) -> Self; fn next_after(&self, other: Self) -> Self; } diff --git a/src/libstd/num/bigint.rs b/src/libstd/num/bigint.rs index cd347098e2511..a5cf929ed93a2 100644 --- a/src/libstd/num/bigint.rs +++ b/src/libstd/num/bigint.rs @@ -831,6 +831,11 @@ impl Signed for BigInt { } } + #[inline(always)] + fn abs_sub(&self, other: &BigInt) -> BigInt { + if *self <= *other { Zero::zero() } else { *self - *other } + } + #[inline(always)] fn signum(&self) -> BigInt { match self.sign { @@ -1920,6 +1925,15 @@ mod bigint_tests { check(11, 5, 55); } + #[test] + fn test_abs_sub() { + assert_eq!((-One::one::()).abs_sub(&One::one()), Zero::zero()); + assert_eq!(One::one::().abs_sub(&One::one()), Zero::zero()); + assert_eq!(One::one::().abs_sub(&Zero::zero()), One::one()); + assert_eq!(One::one::().abs_sub(&-One::one::()), + IntConvertible::from_int(2)); + } + #[test] fn test_to_str_radix() { fn check(n: int, ans: &str) {