Skip to content

Commit 847552f

Browse files
committed
auto merge of #6301 : bjz/rust/numeric-traits, r=pcwalton
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
2 parents 3a34e93 + cc51186 commit 847552f

File tree

7 files changed

+422
-76
lines changed

7 files changed

+422
-76
lines changed

src/libcore/num/cmath.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ pub mod c_double_utils {
3333
unsafe fn erf(n: c_double) -> c_double;
3434
unsafe fn erfc(n: c_double) -> c_double;
3535
unsafe fn exp(n: c_double) -> c_double;
36-
unsafe fn expm1(n: c_double) -> c_double;
36+
// rename: for consistency with underscore usage elsewhere
37+
#[link_name="expm1"] unsafe fn exp_m1(n: c_double) -> c_double;
3738
unsafe fn exp2(n: c_double) -> c_double;
3839
#[link_name="fabs"] unsafe fn abs(n: c_double) -> c_double;
3940
// rename: for clarity and consistency with add/sub/mul/div
@@ -63,7 +64,7 @@ pub mod c_double_utils {
6364
// renamed: "logb" /often/ is confused for log2 by beginners
6465
#[link_name="logb"] unsafe fn log_radix(n: c_double) -> c_double;
6566
// renamed: to be consitent with log as ln
66-
#[link_name="log1p"] unsafe fn ln1p(n: c_double) -> c_double;
67+
#[link_name="log1p"] unsafe fn ln_1p(n: c_double) -> c_double;
6768
unsafe fn log10(n: c_double) -> c_double;
6869
unsafe fn log2(n: c_double) -> c_double;
6970
#[link_name="ilogb"] unsafe fn ilog_radix(n: c_double) -> c_int;
@@ -117,7 +118,7 @@ pub mod c_float_utils {
117118
#[link_name="erff"] unsafe fn erf(n: c_float) -> c_float;
118119
#[link_name="erfcf"] unsafe fn erfc(n: c_float) -> c_float;
119120
#[link_name="expf"] unsafe fn exp(n: c_float) -> c_float;
120-
#[link_name="expm1f"]unsafe fn expm1(n: c_float) -> c_float;
121+
#[link_name="expm1f"]unsafe fn exp_m1(n: c_float) -> c_float;
121122
#[link_name="exp2f"] unsafe fn exp2(n: c_float) -> c_float;
122123
#[link_name="fabsf"] unsafe fn abs(n: c_float) -> c_float;
123124
#[link_name="fdimf"]
@@ -148,7 +149,7 @@ pub mod c_float_utils {
148149

149150
#[link_name="logf"] unsafe fn ln(n: c_float) -> c_float;
150151
#[link_name="logbf"] unsafe fn log_radix(n: c_float) -> c_float;
151-
#[link_name="log1pf"] unsafe fn ln1p(n: c_float) -> c_float;
152+
#[link_name="log1pf"] unsafe fn ln_1p(n: c_float) -> c_float;
152153
#[link_name="log2f"] unsafe fn log2(n: c_float) -> c_float;
153154
#[link_name="log10f"] unsafe fn log10(n: c_float) -> c_float;
154155
#[link_name="ilogbf"] unsafe fn ilog_radix(n: c_float) -> c_int;

src/libcore/num/f32.rs

+123-24
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//! Operations and constants for `f32`
1212
1313
use num::{Zero, One, strconv};
14+
use num::{FPCategory, FPNaN, FPInfinite , FPZero, FPSubnormal, FPNormal};
1415
use prelude::*;
1516

1617
pub use cmath::c_float_targ_consts::*;
@@ -82,7 +83,7 @@ delegate!(
8283
fn cosh(n: c_float) -> c_float = c_float_utils::cosh,
8384
fn erf(n: c_float) -> c_float = c_float_utils::erf,
8485
fn erfc(n: c_float) -> c_float = c_float_utils::erfc,
85-
fn expm1(n: c_float) -> c_float = c_float_utils::expm1,
86+
fn exp_m1(n: c_float) -> c_float = c_float_utils::exp_m1,
8687
fn abs_sub(a: c_float, b: c_float) -> c_float = c_float_utils::abs_sub,
8788
fn fmax(a: c_float, b: c_float) -> c_float = c_float_utils::fmax,
8889
fn fmin(a: c_float, b: c_float) -> c_float = c_float_utils::fmin,
@@ -92,7 +93,7 @@ delegate!(
9293
fn ldexp(x: c_float, n: c_int) -> c_float = c_float_utils::ldexp,
9394
fn lgamma(n: c_float, sign: &mut c_int) -> c_float = c_float_utils::lgamma,
9495
fn log_radix(n: c_float) -> c_float = c_float_utils::log_radix,
95-
fn ln1p(n: c_float) -> c_float = c_float_utils::ln1p,
96+
fn ln_1p(n: c_float) -> c_float = c_float_utils::ln_1p,
9697
fn ilog_radix(n: c_float) -> c_int = c_float_utils::ilog_radix,
9798
fn modf(n: c_float, iptr: &mut c_float) -> c_float = c_float_utils::modf,
9899
fn round(n: c_float) -> c_float = c_float_utils::round,
@@ -195,11 +196,6 @@ pub mod consts {
195196
pub static ln_10: f32 = 2.30258509299404568401799145468436421_f32;
196197
}
197198

198-
#[inline(always)]
199-
pub fn logarithm(n: f32, b: f32) -> f32 {
200-
return log2(n) / log2(b);
201-
}
202-
203199
impl Num for f32 {}
204200

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

316+
///
317+
/// The positive difference of two numbers. Returns `0.0` if the number is less than or
318+
/// equal to `other`, otherwise the difference between`self` and `other` is returned.
319+
///
320+
#[inline(always)]
321+
fn abs_sub(&self, other: &f32) -> f32 { abs_sub(*self, *other) }
322+
320323
///
321324
/// # Returns
322325
///
@@ -413,21 +416,27 @@ impl Trigonometric for f32 {
413416
}
414417

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

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

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

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

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

439+
/// Returns the base 10 logarithm of the number
431440
#[inline(always)]
432441
fn log10(&self) -> f32 { log10(*self) }
433442
}
@@ -504,13 +513,13 @@ impl Real for f32 {
504513
#[inline(always)]
505514
fn log10_e() -> f32 { 0.434294481903251827651128918916605082 }
506515

507-
/// log(2.0)
516+
/// ln(2.0)
508517
#[inline(always)]
509-
fn log_2() -> f32 { 0.693147180559945309417232121458176568 }
518+
fn ln_2() -> f32 { 0.693147180559945309417232121458176568 }
510519

511-
/// log(10.0)
520+
/// ln(10.0)
512521
#[inline(always)]
513-
fn log_10() -> f32 { 2.30258509299404568401799145468436421 }
522+
fn ln_10() -> f32 { 2.30258509299404568401799145468436421 }
514523

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

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

566+
/// Returns `true` if the number is infinite
567+
#[inline(always)]
568+
fn is_infinite(&self) -> bool {
569+
*self == Float::infinity() || *self == Float::neg_infinity()
570+
}
571+
572+
/// Returns `true` if the number is neither infinite or NaN
573+
#[inline(always)]
574+
fn is_finite(&self) -> bool {
575+
!(self.is_NaN() || self.is_infinite())
576+
}
577+
578+
/// Returns `true` if the number is neither zero, infinite, subnormal or NaN
579+
#[inline(always)]
580+
fn is_normal(&self) -> bool {
581+
match self.classify() {
582+
FPNormal => true,
583+
_ => false,
584+
}
585+
}
586+
587+
/// Returns the floating point category of the number. If only one property is going to
588+
/// be tested, it is generally faster to use the specific predicate instead.
589+
fn classify(&self) -> FPCategory {
590+
static EXP_MASK: u32 = 0x7f800000;
591+
static MAN_MASK: u32 = 0x007fffff;
592+
593+
match (
594+
unsafe { ::cast::transmute::<f32,u32>(*self) } & EXP_MASK,
595+
unsafe { ::cast::transmute::<f32,u32>(*self) } & MAN_MASK
596+
) {
597+
(EXP_MASK, 0) => FPInfinite,
598+
(EXP_MASK, _) => FPNaN,
599+
(exp, _) if exp != 0 => FPNormal,
600+
_ if self.is_zero() => FPZero,
601+
_ => FPSubnormal,
602+
}
603+
}
604+
556605
#[inline(always)]
557606
fn mantissa_digits() -> uint { 24 }
558607

@@ -574,17 +623,19 @@ impl Float for f32 {
574623
#[inline(always)]
575624
fn max_10_exp() -> int { 38 }
576625

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

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

589640
///
590641
/// Fused multiply-add. Computes `(self * a) + b` with only one rounding error. This
@@ -823,6 +874,7 @@ impl num::FromStrRadix for f32 {
823874
#[cfg(test)]
824875
mod tests {
825876
use f32::*;
877+
use num::*;
826878
use super::*;
827879
use prelude::*;
828880

@@ -938,12 +990,12 @@ mod tests {
938990
assert_approx_eq!(Real::frac_1_sqrt2::<f32>(), 1f32 / 2f32.sqrt());
939991
assert_approx_eq!(Real::log2_e::<f32>(), Real::e::<f32>().log2());
940992
assert_approx_eq!(Real::log10_e::<f32>(), Real::e::<f32>().log10());
941-
assert_approx_eq!(Real::log_2::<f32>(), 2f32.log());
942-
assert_approx_eq!(Real::log_10::<f32>(), 10f32.log());
993+
assert_approx_eq!(Real::ln_2::<f32>(), 2f32.ln());
994+
assert_approx_eq!(Real::ln_10::<f32>(), 10f32.ln());
943995
}
944996

945997
#[test]
946-
pub fn test_signed() {
998+
pub fn test_abs() {
947999
assert_eq!(infinity.abs(), infinity);
9481000
assert_eq!(1f32.abs(), 1f32);
9491001
assert_eq!(0f32.abs(), 0f32);
@@ -952,7 +1004,24 @@ mod tests {
9521004
assert_eq!(neg_infinity.abs(), infinity);
9531005
assert_eq!((1f32/neg_infinity).abs(), 0f32);
9541006
assert!(NaN.abs().is_NaN());
1007+
}
1008+
1009+
#[test]
1010+
fn test_abs_sub() {
1011+
assert_eq!((-1f32).abs_sub(&1f32), 0f32);
1012+
assert_eq!(1f32.abs_sub(&1f32), 0f32);
1013+
assert_eq!(1f32.abs_sub(&0f32), 1f32);
1014+
assert_eq!(1f32.abs_sub(&-1f32), 2f32);
1015+
assert_eq!(neg_infinity.abs_sub(&0f32), 0f32);
1016+
assert_eq!(infinity.abs_sub(&1f32), infinity);
1017+
assert_eq!(0f32.abs_sub(&neg_infinity), infinity);
1018+
assert_eq!(0f32.abs_sub(&infinity), 0f32);
1019+
assert!(NaN.abs_sub(&-1f32).is_NaN());
1020+
assert!(1f32.abs_sub(&NaN).is_NaN());
1021+
}
9551022

1023+
#[test]
1024+
fn test_signum() {
9561025
assert_eq!(infinity.signum(), 1f32);
9571026
assert_eq!(1f32.signum(), 1f32);
9581027
assert_eq!(0f32.signum(), 1f32);
@@ -961,7 +1030,10 @@ mod tests {
9611030
assert_eq!(neg_infinity.signum(), -1f32);
9621031
assert_eq!((1f32/neg_infinity).signum(), -1f32);
9631032
assert!(NaN.signum().is_NaN());
1033+
}
9641034

1035+
#[test]
1036+
fn test_is_positive() {
9651037
assert!(infinity.is_positive());
9661038
assert!(1f32.is_positive());
9671039
assert!(0f32.is_positive());
@@ -970,7 +1042,10 @@ mod tests {
9701042
assert!(!neg_infinity.is_positive());
9711043
assert!(!(1f32/neg_infinity).is_positive());
9721044
assert!(!NaN.is_positive());
1045+
}
9731046

1047+
#[test]
1048+
fn test_is_negative() {
9741049
assert!(!infinity.is_negative());
9751050
assert!(!1f32.is_negative());
9761051
assert!(!0f32.is_negative());
@@ -995,4 +1070,28 @@ mod tests {
9951070
assert_eq!(Primitive::bits::<f32>(), sys::size_of::<f32>() * 8);
9961071
assert_eq!(Primitive::bytes::<f32>(), sys::size_of::<f32>());
9971072
}
1073+
1074+
#[test]
1075+
fn test_is_normal() {
1076+
assert!(!Float::NaN::<f32>().is_normal());
1077+
assert!(!Float::infinity::<f32>().is_normal());
1078+
assert!(!Float::neg_infinity::<f32>().is_normal());
1079+
assert!(!Zero::zero::<f32>().is_normal());
1080+
assert!(!Float::neg_zero::<f32>().is_normal());
1081+
assert!(1f32.is_normal());
1082+
assert!(1e-37f32.is_normal());
1083+
assert!(!1e-38f32.is_normal());
1084+
}
1085+
1086+
#[test]
1087+
fn test_classify() {
1088+
assert_eq!(Float::NaN::<f32>().classify(), FPNaN);
1089+
assert_eq!(Float::infinity::<f32>().classify(), FPInfinite);
1090+
assert_eq!(Float::neg_infinity::<f32>().classify(), FPInfinite);
1091+
assert_eq!(Zero::zero::<f32>().classify(), FPZero);
1092+
assert_eq!(Float::neg_zero::<f32>().classify(), FPZero);
1093+
assert_eq!(1f32.classify(), FPNormal);
1094+
assert_eq!(1e-37f32.classify(), FPNormal);
1095+
assert_eq!(1e-38f32.classify(), FPSubnormal);
1096+
}
9981097
}

0 commit comments

Comments
 (0)