diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index bc532990e94..a53b8535213 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -957,4 +957,46 @@ pub fn acosh(self) -> f32 { pub fn atanh(self) -> f32 { 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() } + + /// Gamma function. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_gamma)] + /// let x = 5.0f32; + /// + /// let abs_difference = (x.gamma() - 24.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_gamma", issue = "99842")] + #[inline] + pub fn gamma(self) -> f32 { + unsafe { cmath::tgammaf(self) } + } + + /// Returns the natural logarithm of the gamma function. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_gamma)] + /// let x = 2.0f32; + /// + /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_gamma", issue = "99842")] + #[inline] + pub fn ln_gamma(self) -> (f32, i32) { + let mut signgamp: i32 = 0; + let x = unsafe { cmath::lgammaf_r(self, &mut signgamp) }; + (x, signgamp) + } } diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs index e949def00bb..9ca4e8f2f45 100644 --- a/library/std/src/f32/tests.rs +++ b/library/std/src/f32/tests.rs @@ -652,6 +652,38 @@ fn test_atanh() { assert_approx_eq!((-0.5f32).atanh(), -0.54930614433405484569762261846126285f32); } +#[test] +fn test_gamma() { + // precision can differ between platforms + assert_approx_eq!(1.0f32.gamma(), 1.0f32); + assert_approx_eq!(2.0f32.gamma(), 1.0f32); + assert_approx_eq!(3.0f32.gamma(), 2.0f32); + assert_approx_eq!(4.0f32.gamma(), 6.0f32); + assert_approx_eq!(5.0f32.gamma(), 24.0f32); + assert_approx_eq!(0.5f32.gamma(), consts::PI.sqrt()); + assert_approx_eq!((-0.5f32).gamma(), -2.0 * consts::PI.sqrt()); + assert_eq!(0.0f32.gamma(), f32::INFINITY); + assert_eq!((-0.0f32).gamma(), f32::NEG_INFINITY); + assert!((-1.0f32).gamma().is_nan()); + assert!((-2.0f32).gamma().is_nan()); + assert!(f32::NAN.gamma().is_nan()); + assert!(f32::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f32::INFINITY.gamma(), f32::INFINITY); + assert_eq!(171.71f32.gamma(), f32::INFINITY); +} + +#[test] +fn test_ln_gamma() { + assert_approx_eq!(1.0f32.ln_gamma().0, 0.0f32); + assert_eq!(1.0f32.ln_gamma().1, 1); + assert_approx_eq!(2.0f32.ln_gamma().0, 0.0f32); + assert_eq!(2.0f32.ln_gamma().1, 1); + assert_approx_eq!(3.0f32.ln_gamma().0, 2.0f32.ln()); + assert_eq!(3.0f32.ln_gamma().1, 1); + assert_approx_eq!((-0.5f32).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln()); + assert_eq!((-0.5f32).ln_gamma().1, -1); +} + #[test] fn test_real_consts() { use super::consts; diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 5af9f8bcb23..a1cec22c97a 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -957,4 +957,46 @@ pub fn acosh(self) -> f64 { pub fn atanh(self) -> f64 { 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() } + + /// Gamma function. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_gamma)] + /// let x = 5.0f64; + /// + /// let abs_difference = (x.gamma() - 24.0).abs(); + /// + /// assert!(abs_difference <= f64::EPSILON); + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_gamma", issue = "99842")] + #[inline] + pub fn gamma(self) -> f64 { + unsafe { cmath::tgamma(self) } + } + + /// Returns the natural logarithm of the gamma function. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_gamma)] + /// let x = 2.0f64; + /// + /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); + /// + /// assert!(abs_difference <= f64::EPSILON); + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_gamma", issue = "99842")] + #[inline] + pub fn ln_gamma(self) -> (f64, i32) { + let mut signgamp: i32 = 0; + let x = unsafe { cmath::lgamma_r(self, &mut signgamp) }; + (x, signgamp) + } } diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs index 53d351cceef..f88d01593b5 100644 --- a/library/std/src/f64/tests.rs +++ b/library/std/src/f64/tests.rs @@ -635,6 +635,38 @@ fn test_atanh() { assert_approx_eq!((-0.5f64).atanh(), -0.54930614433405484569762261846126285f64); } +#[test] +fn test_gamma() { + // precision can differ between platforms + assert_approx_eq!(1.0f64.gamma(), 1.0f64); + assert_approx_eq!(2.0f64.gamma(), 1.0f64); + assert_approx_eq!(3.0f64.gamma(), 2.0f64); + assert_approx_eq!(4.0f64.gamma(), 6.0f64); + assert_approx_eq!(5.0f64.gamma(), 24.0f64); + assert_approx_eq!(0.5f64.gamma(), consts::PI.sqrt()); + assert_approx_eq!((-0.5f64).gamma(), -2.0 * consts::PI.sqrt()); + assert_eq!(0.0f64.gamma(), f64::INFINITY); + assert_eq!((-0.0f64).gamma(), f64::NEG_INFINITY); + assert!((-1.0f64).gamma().is_nan()); + assert!((-2.0f64).gamma().is_nan()); + assert!(f64::NAN.gamma().is_nan()); + assert!(f64::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f64::INFINITY.gamma(), f64::INFINITY); + assert_eq!(171.71f64.gamma(), f64::INFINITY); +} + +#[test] +fn test_ln_gamma() { + assert_approx_eq!(1.0f64.ln_gamma().0, 0.0f64); + assert_eq!(1.0f64.ln_gamma().1, 1); + assert_approx_eq!(2.0f64.ln_gamma().0, 0.0f64); + assert_eq!(2.0f64.ln_gamma().1, 1); + assert_approx_eq!(3.0f64.ln_gamma().0, 2.0f64.ln()); + assert_eq!(3.0f64.ln_gamma().1, 1); + assert_approx_eq!((-0.5f64).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln()); + assert_eq!((-0.5f64).ln_gamma().1, -1); +} + #[test] fn test_real_consts() { use super::consts; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 4cd251d0ac2..c54b65af67c 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -286,6 +286,7 @@ #![feature(exact_size_is_empty)] #![feature(exclusive_wrapper)] #![feature(extend_one)] +#![feature(float_gamma)] #![feature(float_minimum_maximum)] #![feature(float_next_up_down)] #![feature(hasher_prefixfree_extras)] diff --git a/library/std/src/sys/unix/cmath.rs b/library/std/src/sys/unix/cmath.rs index 2bf80d7a4cb..5346d229116 100644 --- a/library/std/src/sys/unix/cmath.rs +++ b/library/std/src/sys/unix/cmath.rs @@ -30,4 +30,8 @@ pub fn tanf(n: f32) -> f32; pub fn tanh(n: f64) -> f64; pub fn tanhf(n: f32) -> f32; + pub fn tgamma(n: f64) -> f64; + pub fn tgammaf(n: f32) -> f32; + pub fn lgamma_r(n: f64, s: &mut i32) -> f64; + pub fn lgammaf_r(n: f32, s: &mut i32) -> f32; } diff --git a/library/std/src/sys/windows/cmath.rs b/library/std/src/sys/windows/cmath.rs index 43ab8c7ee65..1b2a86f3c0e 100644 --- a/library/std/src/sys/windows/cmath.rs +++ b/library/std/src/sys/windows/cmath.rs @@ -1,6 +1,6 @@ #![cfg(not(test))] -use libc::{c_double, c_float}; +use libc::{c_double, c_float, c_int}; extern "C" { pub fn acos(n: c_double) -> c_double; @@ -23,6 +23,10 @@ pub fn sinh(n: c_double) -> c_double; pub fn tan(n: c_double) -> c_double; pub fn tanh(n: c_double) -> c_double; + pub fn tgamma(n: c_double) -> c_double; + pub fn tgammaf(n: c_float) -> c_float; + pub fn lgamma_r(n: c_double, s: &mut c_int) -> c_double; + pub fn lgammaf_r(n: c_float, s: &mut c_int) -> c_float; } pub use self::shims::*;