Add inverse hyperbolic functions

This commit is contained in:
Brendan Zabarauskas 2013-05-14 00:11:35 +10:00
parent 830b945a9d
commit 8d4d2b00c5
4 changed files with 263 additions and 0 deletions

View File

@ -450,6 +450,58 @@ impl Hyperbolic for f32 {
#[inline(always)]
fn tanh(&self) -> f32 { tanh(*self) }
///
/// Inverse hyperbolic sine
///
/// # Returns
///
/// - on success, the inverse hyperbolic sine of `self` will be returned
/// - `self` if `self` is `0.0`, `-0.0`, `infinity`, or `neg_infinity`
/// - `NaN` if `self` is `NaN`
///
#[inline(always)]
fn asinh(&self) -> f32 {
match *self {
infinity => infinity,
neg_infinity => neg_infinity,
x => (x + ((x * x) + 1.0).sqrt()).ln(),
}
}
///
/// Inverse hyperbolic cosine
///
/// # Returns
///
/// - on success, the inverse hyperbolic cosine of `self` will be returned
/// - `infinity` if `self` is `infinity`
/// - `NaN` if `self` is `NaN` or `self < 1.0` (including `neg_infinity`)
///
#[inline(always)]
fn acosh(&self) -> f32 {
match *self {
x if x < 1.0 => Float::NaN(),
x => (x + ((x * x) - 1.0).sqrt()).ln(),
}
}
///
/// Inverse hyperbolic tangent
///
/// # Returns
///
/// - on success, the inverse hyperbolic tangent of `self` will be returned
/// - `self` if `self` is `0.0` or `-0.0`
/// - `infinity` if `self` is `1.0`
/// - `neg_infinity` if `self` is `-1.0`
/// - `NaN` if the `self` is `NaN` or outside the domain of `-1.0 <= self <= 1.0`
/// (including `infinity` and `neg_infinity`)
///
#[inline(always)]
fn atanh(&self) -> f32 {
0.5 * ((2.0 * *self) / (1.0 - *self)).ln_1p()
}
}
impl Real for f32 {
@ -972,6 +1024,43 @@ mod tests {
assert_approx_eq!((-1.7f32).fract(), -0.7f32);
}
#[test]
fn test_asinh() {
assert_eq!(0.0f32.asinh(), 0.0f32);
assert_eq!((-0.0f32).asinh(), -0.0f32);
assert_eq!(Float::infinity::<f32>().asinh(), Float::infinity::<f32>());
assert_eq!(Float::neg_infinity::<f32>().asinh(), Float::neg_infinity::<f32>());
assert!(Float::NaN::<f32>().asinh().is_NaN());
assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32);
assert_approx_eq!((-2.0f32).asinh(), -1.443635475178810342493276740273105f32);
}
#[test]
fn test_acosh() {
assert_eq!(1.0f32.acosh(), 0.0f32);
assert!(0.999f32.acosh().is_NaN());
assert_eq!(Float::infinity::<f32>().acosh(), Float::infinity::<f32>());
assert!(Float::neg_infinity::<f32>().acosh().is_NaN());
assert!(Float::NaN::<f32>().acosh().is_NaN());
assert_approx_eq!(2.0f32.acosh(), 1.31695789692481670862504634730796844f32);
assert_approx_eq!(3.0f32.acosh(), 1.76274717403908605046521864995958461f32);
}
#[test]
fn test_atanh() {
assert_eq!(0.0f32.atanh(), 0.0f32);
assert_eq!((-0.0f32).atanh(), -0.0f32);
assert_eq!(1.0f32.atanh(), Float::infinity::<f32>());
assert_eq!((-1.0f32).atanh(), Float::neg_infinity::<f32>());
assert!(2f64.atanh().atanh().is_NaN());
assert!((-2f64).atanh().atanh().is_NaN());
assert!(Float::infinity::<f64>().atanh().is_NaN());
assert!(Float::neg_infinity::<f64>().atanh().is_NaN());
assert!(Float::NaN::<f32>().atanh().is_NaN());
assert_approx_eq!(0.5f32.atanh(), 0.54930614433405484569762261846126285f32);
assert_approx_eq!((-0.5f32).atanh(), -0.54930614433405484569762261846126285f32);
}
#[test]
fn test_real_consts() {
assert_approx_eq!(Real::two_pi::<f32>(), 2f32 * Real::pi::<f32>());

View File

@ -463,6 +463,58 @@ impl Hyperbolic for f64 {
#[inline(always)]
fn tanh(&self) -> f64 { tanh(*self) }
///
/// Inverse hyperbolic sine
///
/// # Returns
///
/// - on success, the inverse hyperbolic sine of `self` will be returned
/// - `self` if `self` is `0.0`, `-0.0`, `infinity`, or `neg_infinity`
/// - `NaN` if `self` is `NaN`
///
#[inline(always)]
fn asinh(&self) -> f64 {
match *self {
infinity => infinity,
neg_infinity => neg_infinity,
x => (x + ((x * x) + 1.0).sqrt()).ln(),
}
}
///
/// Inverse hyperbolic cosine
///
/// # Returns
///
/// - on success, the inverse hyperbolic cosine of `self` will be returned
/// - `infinity` if `self` is `infinity`
/// - `NaN` if `self` is `NaN` or `self < 1.0` (including `neg_infinity`)
///
#[inline(always)]
fn acosh(&self) -> f64 {
match *self {
x if x < 1.0 => Float::NaN(),
x => (x + ((x * x) - 1.0).sqrt()).ln(),
}
}
///
/// Inverse hyperbolic tangent
///
/// # Returns
///
/// - on success, the inverse hyperbolic tangent of `self` will be returned
/// - `self` if `self` is `0.0` or `-0.0`
/// - `infinity` if `self` is `1.0`
/// - `neg_infinity` if `self` is `-1.0`
/// - `NaN` if the `self` is `NaN` or outside the domain of `-1.0 <= self <= 1.0`
/// (including `infinity` and `neg_infinity`)
///
#[inline(always)]
fn atanh(&self) -> f64 {
0.5 * ((2.0 * *self) / (1.0 - *self)).ln_1p()
}
}
impl Real for f64 {
@ -1019,6 +1071,43 @@ mod tests {
assert_approx_eq!((-1.7f64).fract(), -0.7f64);
}
#[test]
fn test_asinh() {
assert_eq!(0.0f64.asinh(), 0.0f64);
assert_eq!((-0.0f64).asinh(), -0.0f64);
assert_eq!(Float::infinity::<f64>().asinh(), Float::infinity::<f64>());
assert_eq!(Float::neg_infinity::<f64>().asinh(), Float::neg_infinity::<f64>());
assert!(Float::NaN::<f64>().asinh().is_NaN());
assert_approx_eq!(2.0f64.asinh(), 1.443635475178810342493276740273105f64);
assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64);
}
#[test]
fn test_acosh() {
assert_eq!(1.0f64.acosh(), 0.0f64);
assert!(0.999f64.acosh().is_NaN());
assert_eq!(Float::infinity::<f64>().acosh(), Float::infinity::<f64>());
assert!(Float::neg_infinity::<f64>().acosh().is_NaN());
assert!(Float::NaN::<f64>().acosh().is_NaN());
assert_approx_eq!(2.0f64.acosh(), 1.31695789692481670862504634730796844f64);
assert_approx_eq!(3.0f64.acosh(), 1.76274717403908605046521864995958461f64);
}
#[test]
fn test_atanh() {
assert_eq!(0.0f64.atanh(), 0.0f64);
assert_eq!((-0.0f64).atanh(), -0.0f64);
assert_eq!(1.0f64.atanh(), Float::infinity::<f64>());
assert_eq!((-1.0f64).atanh(), Float::neg_infinity::<f64>());
assert!(2f64.atanh().atanh().is_NaN());
assert!((-2f64).atanh().atanh().is_NaN());
assert!(Float::infinity::<f64>().atanh().is_NaN());
assert!(Float::neg_infinity::<f64>().atanh().is_NaN());
assert!(Float::NaN::<f64>().atanh().is_NaN());
assert_approx_eq!(0.5f64.atanh(), 0.54930614433405484569762261846126285f64);
assert_approx_eq!((-0.5f64).atanh(), -0.54930614433405484569762261846126285f64);
}
#[test]
fn test_real_consts() {
assert_approx_eq!(Real::two_pi::<f64>(), 2.0 * Real::pi::<f64>());

View File

@ -585,6 +585,51 @@ impl Hyperbolic for float {
fn tanh(&self) -> float {
(*self as f64).tanh() as float
}
///
/// Inverse hyperbolic sine
///
/// # Returns
///
/// - on success, the inverse hyperbolic sine of `self` will be returned
/// - `self` if `self` is `0.0`, `-0.0`, `infinity`, or `neg_infinity`
/// - `NaN` if `self` is `NaN`
///
#[inline(always)]
fn asinh(&self) -> float {
(*self as f64).asinh() as float
}
///
/// Inverse hyperbolic cosine
///
/// # Returns
///
/// - on success, the inverse hyperbolic cosine of `self` will be returned
/// - `infinity` if `self` is `infinity`
/// - `NaN` if `self` is `NaN` or `self < 1.0` (including `neg_infinity`)
///
#[inline(always)]
fn acosh(&self) -> float {
(*self as f64).acosh() as float
}
///
/// Inverse hyperbolic tangent
///
/// # Returns
///
/// - on success, the inverse hyperbolic tangent of `self` will be returned
/// - `self` if `self` is `0.0` or `-0.0`
/// - `infinity` if `self` is `1.0`
/// - `neg_infinity` if `self` is `-1.0`
/// - `NaN` if the `self` is `NaN` or outside the domain of `-1.0 <= self <= 1.0`
/// (including `infinity` and `neg_infinity`)
///
#[inline(always)]
fn atanh(&self) -> float {
(*self as f64).atanh() as float
}
}
impl Real for float {
@ -972,6 +1017,43 @@ mod tests {
assert_approx_eq!((-1.7f).fract(), -0.7f);
}
#[test]
fn test_asinh() {
assert_eq!(0.0f.asinh(), 0.0f);
assert_eq!((-0.0f).asinh(), -0.0f);
assert_eq!(Float::infinity::<float>().asinh(), Float::infinity::<float>());
assert_eq!(Float::neg_infinity::<float>().asinh(), Float::neg_infinity::<float>());
assert!(Float::NaN::<float>().asinh().is_NaN());
assert_approx_eq!(2.0f.asinh(), 1.443635475178810342493276740273105f);
assert_approx_eq!((-2.0f).asinh(), -1.443635475178810342493276740273105f);
}
#[test]
fn test_acosh() {
assert_eq!(1.0f.acosh(), 0.0f);
assert!(0.999f.acosh().is_NaN());
assert_eq!(Float::infinity::<float>().acosh(), Float::infinity::<float>());
assert!(Float::neg_infinity::<float>().acosh().is_NaN());
assert!(Float::NaN::<float>().acosh().is_NaN());
assert_approx_eq!(2.0f.acosh(), 1.31695789692481670862504634730796844f);
assert_approx_eq!(3.0f.acosh(), 1.76274717403908605046521864995958461f);
}
#[test]
fn test_atanh() {
assert_eq!(0.0f.atanh(), 0.0f);
assert_eq!((-0.0f).atanh(), -0.0f);
assert_eq!(1.0f.atanh(), Float::infinity::<float>());
assert_eq!((-1.0f).atanh(), Float::neg_infinity::<float>());
assert!(2f64.atanh().atanh().is_NaN());
assert!((-2f64).atanh().atanh().is_NaN());
assert!(Float::infinity::<f64>().atanh().is_NaN());
assert!(Float::neg_infinity::<f64>().atanh().is_NaN());
assert!(Float::NaN::<float>().atanh().is_NaN());
assert_approx_eq!(0.5f.atanh(), 0.54930614433405484569762261846126285f);
assert_approx_eq!((-0.5f).atanh(), -0.54930614433405484569762261846126285f);
}
#[test]
fn test_real_consts() {
assert_approx_eq!(Real::two_pi::<float>(), 2f * Real::pi::<float>());

View File

@ -133,6 +133,9 @@ pub trait Hyperbolic: Exponential {
fn sinh(&self) -> Self;
fn cosh(&self) -> Self;
fn tanh(&self) -> Self;
fn asinh(&self) -> Self;
fn acosh(&self) -> Self;
fn atanh(&self) -> Self;
}
///