///|
/// Compute hyperbolic tangent function of a double-precision floating point number.
///
/// # Examples
///
/// ```moonbit nocheck
/// 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) raise {
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)
}