///| Compute hyperbolic tangent function of a double-precision floating point number.
///
/// # Examples
///
/// ```moonbit
/// assert_eq!(tanh(-0.5), -0.46211715726000974)
/// assert_eq!(tanh(0.5), 0.46211715726000974)
/// assert_eq!(tanh(0.0), 0.0)
/// assert_eq!(tanh(1), 0.7615941559557649)
/// assert_eq!(tanh(2), 0.9640275800758169)
/// ```
///
/// # Special Cases
///
/// 1. tanh(NaN) = NaN
/// 2. tanh(+inf) = 1
/// 3. tanh(-inf) = -1
///
/// # Accuracy
///
/// 2 ulp.
pub fn tanh(x : Double) -> Double {
if isnan(x) {
return x
}
if ispinf(x) {
return 1.0
}
if isninf(x) {
return -1.0
}
let ix = __hi(x).reinterpret_as_int() & 0x7fffffff
let tiny = 1.0e-300
let z = if ix < 0x40360000 {
if ix < 0x3c800000 {
x * (1.0 + x)
} else if ix >= 0x3ff00000 {
let t = expm1(2.0 * fabs(x))
1.0 - 2.0 / (t + 2.0)
} else {
let t = expm1(-2.0 * fabs(x))
-t / (t + 2.0)
}
} else {
1.0 - tiny
}
if x >= 0.0 {
z
} else {
-z
}
}
///|
test "tanh" {
fn assert_tanh_ulp!(input, expect) {
assert_ulp!(expect, tanh(input), TANH_MAX_ULP)
}
assert_tanh_ulp!(-0.8, -0.6640367702678491)
assert_tanh_ulp!(-0.7, -0.6043677771171636)
assert_tanh_ulp!(-0.6, -0.5370495669980353)
assert_tanh_ulp!(-0.5, -0.46211715726000974)
assert_tanh_ulp!(-0.4, -0.3799489622552249)
assert_tanh_ulp!(-0.3, -0.2913126124515909)
assert_tanh_ulp!(-0.2, -0.197375320224904)
assert_tanh_ulp!(-0.1, -0.09966799462495582)
assert_tanh_ulp!(-0, -0)
assert_tanh_ulp!(-3.141592653589793, -0.99627207622075)
assert_tanh_ulp!(-1.5707963267948966, -0.9171523356672744)
assert_tanh_ulp!(-0.7853981633974483, -0.6557942026326724)
assert_tanh_ulp!(0, 0)
assert_tanh_ulp!(0.1, 0.09966799462495582)
assert_tanh_ulp!(0.2, 0.197375320224904)
assert_tanh_ulp!(0.3, 0.2913126124515909)
assert_tanh_ulp!(0.4, 0.3799489622552249)
assert_tanh_ulp!(0.5, 0.46211715726000974)
assert_tanh_ulp!(0.6, 0.5370495669980353)
assert_tanh_ulp!(0.7, 0.6043677771171636)
assert_tanh_ulp!(0.8, 0.6640367702678491)
assert_tanh_ulp!(0.9, 0.7162978701990245)
assert_tanh_ulp!(1, 0.7615941559557649)
assert_tanh_ulp!(3.141592653589793, 0.99627207622075)
assert_tanh_ulp!(1.5707963267948966, 0.9171523356672744)
assert_tanh_ulp!(0.7853981633974483, 0.6557942026326724)
assert_tanh_ulp!(-1, -0.7615941559557649)
assert_tanh_ulp!(-2, -0.9640275800758169)
assert_tanh_ulp!(-3, -0.9950547536867305)
assert_tanh_ulp!(-4, -0.999329299739067)
assert_tanh_ulp!(-5, -0.9999092042625951)
assert_tanh_ulp!(-6, -0.9999877116507956)
assert_tanh_ulp!(-7, -0.9999983369439447)
assert_tanh_ulp!(-8, -0.9999997749296758)
assert_tanh_ulp!(-9, -0.999999969540041)
assert_tanh_ulp!(1, 0.7615941559557649)
assert_tanh_ulp!(2, 0.9640275800758169)
assert_tanh_ulp!(3, 0.9950547536867305)
assert_tanh_ulp!(4, 0.999329299739067)
assert_tanh_ulp!(5, 0.9999092042625951)
assert_tanh_ulp!(6, 0.9999877116507956)
assert_tanh_ulp!(7, 0.9999983369439447)
assert_tanh_ulp!(8, 0.9999997749296758)
assert_tanh_ulp!(9, 0.999999969540041)
assert_tanh_ulp!(10, 0.9999999958776927)
assert_tanh_ulp!(100, 1)
assert_tanh_ulp!(1000, 1)
assert_tanh_ulp!(10000, 1)
assert_tanh_ulp!(2.5, 0.9866142981514303)
assert_tanh_ulp!(3.4, 0.9977749279342794)
assert_tanh_ulp!(5.3, 0.9999501692221211)
assert_tanh_ulp!(6.2, 0.9999917628565104)
assert_tanh_ulp!(7.1, 0.9999986384046582)
assert_tanh_ulp!(8.9, 0.9999999627961221)
assert_tanh_ulp!(9.8, 0.9999999938502403)
assert_tanh_ulp!(10.7, 0.9999999989834516)
assert_tanh_ulp!(101.6, 1)
assert_tanh_ulp!(1.542, 0.9124558296639592)
assert_tanh_ulp!(2.846, 0.9932769945490828)
assert_tanh_ulp!(7.881, 0.9999997144512194)
assert_tanh_ulp!(3.772, 0.9989420074167165)
assert_tanh_ulp!(-1.542, -0.9124558296639592)
assert_tanh_ulp!(-2.846, -0.9932769945490828)
assert_tanh_ulp!(-7.881, -0.9999997144512194)
assert_tanh_ulp!(-3.772, -0.9989420074167165)
assert_tanh_ulp!(-1, -0.7615941559557649)
assert_tanh_ulp!(0, 0)
assert_tanh_ulp!(-0, -0)
assert_tanh_ulp!(@double.not_a_number, @double.not_a_number)
assert_tanh_ulp!(@double.infinity, 1)
assert_tanh_ulp!(@double.neg_infinity, -1)
}