blob: d4a11026fb1c1de01906133ffdc6e661f8d49dba [file]
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/* Refer to the exp routine for the underlying algorithm */
#if __CLC_FPSIZE == 32
_CLC_OVERLOAD _CLC_CONST _CLC_DEF __CLC_FLOATN __clc_expm1(__CLC_FLOATN x) {
const __CLC_FLOATN x_max = 0x1.62e42ep+6f; // 64 * ln(2)
const __CLC_FLOATN ln2_tail =
-0x1.05c610p-29f; // small correction term for ln(2)
const __CLC_FLOATN c1 = 0x1.a26762p-13f; // ~1.250000e-4
const __CLC_FLOATN c2 = 0x1.6d2e00p-10f; // ~ 9.999999e-4
const __CLC_FLOATN c3 = 0x1.110ff2p-7f; // ~8.333333e-3
const __CLC_FLOATN c4 = 0x1.555502p-5f; // ~4.166667e-2
const __CLC_FLOATN c5 = 0x1.555556p-3f; // ~1.666667e-1
__CLC_FLOATN fn = __clc_rint(x * (__CLC_FLOATN)M_LOG2E);
__CLC_FLOATN t =
__clc_fma(-fn, ln2_tail, __clc_fma(-fn, (__CLC_FLOATN)M_LN2, x));
__CLC_FLOATN q0 = __clc_mad(t, c1, c2);
__CLC_FLOATN q1 = __clc_mad(t, q0, c3);
__CLC_FLOATN q2 = __clc_mad(t, q1, c4);
__CLC_FLOATN q3 = __clc_mad(t, q2, c5);
__CLC_FLOATN p = __clc_fma(t, t * __clc_mad(t, q3, 0.5f), t);
__CLC_INTN e = fn == 128.0f ? 127 : __CLC_CONVERT_INTN(fn);
__CLC_FLOATN s = __clc_ldexp(__CLC_FP_LIT(1.0), e);
__CLC_FLOATN z = __clc_fma(s, p, s - 1.0f);
z = fn == 128.0f ? 2.0f * z : z;
z = x > x_max ? __CLC_GENTYPE_INF : z;
z = x < -17.0f ? -1.0f : z;
return z;
}
#elif __CLC_FPSIZE == 64
_CLC_OVERLOAD _CLC_CONST _CLC_DEF __CLC_DOUBLEN __clc_expm1(__CLC_DOUBLEN x) {
const __CLC_DOUBLEN max_expm1_arg = 0x1.62e42fefa39efp+9;
const __CLC_DOUBLEN min_expm1_arg = -37.0;
const __CLC_DOUBLEN lnof2_by_64_tail = 0x1.abc9e3b39803fp-56;
__CLC_DOUBLEN dn = __clc_rint(x * M_LOG2E);
// Reduced argument t = x - dn * (ln2/64), split into head and tail for
// precision
__CLC_DOUBLEN t = __clc_mad(-dn, lnof2_by_64_tail, __clc_mad(-dn, M_LN2, x));
const __CLC_DOUBLEN c0 = 0x1.1f32ea9d67f34p-29;
const __CLC_DOUBLEN c1 = 0x1.af4eb2a1b768bp-26;
const __CLC_DOUBLEN c2 = 0x1.27e500e0ac05bp-22;
const __CLC_DOUBLEN c3 = 0x1.71de01b889c29p-19;
const __CLC_DOUBLEN c4 = 0x1.a01a0197bcfd8p-16;
const __CLC_DOUBLEN c5 = 0x1.a01a01ac1a723p-13;
const __CLC_DOUBLEN c6 = 0x1.6c16c16c18931p-10;
const __CLC_DOUBLEN c7 = 0x1.1111111110056p-7;
const __CLC_DOUBLEN c8 = 0x1.5555555555552p-5;
const __CLC_DOUBLEN c9 = 0x1.5555555555557p-3;
const __CLC_DOUBLEN c10 = 0x1.0000000000000p-1;
__CLC_DOUBLEN q0 = __clc_mad(t, c0, c1);
__CLC_DOUBLEN q1 = __clc_mad(t, q0, c2);
__CLC_DOUBLEN q2 = __clc_mad(t, q1, c3);
__CLC_DOUBLEN q3 = __clc_mad(t, q2, c4);
__CLC_DOUBLEN q4 = __clc_mad(t, q3, c5);
__CLC_DOUBLEN q5 = __clc_mad(t, q4, c6);
__CLC_DOUBLEN q6 = __clc_mad(t, q5, c7);
__CLC_DOUBLEN q7 = __clc_mad(t, q6, c8);
__CLC_DOUBLEN q8 = __clc_mad(t, q7, c9);
__CLC_DOUBLEN p = __clc_mad(t, t * __clc_mad(t, q8, c10), t);
__CLC_INTN e =
__CLC_CONVERT_INTN(dn == 1024.0) ? 1023 : __CLC_CONVERT_INTN(dn);
// s = 2^e
__CLC_DOUBLEN s = __clc_ldexp(__CLC_FP_LIT(1.0), e);
// expm1(x) ~= s * p + (s - 1)
__CLC_DOUBLEN z = __clc_mad(s, p, s - 1.0);
z = dn == 1024.0 ? 2.0 * z : z;
z = x > max_expm1_arg ? __CLC_GENTYPE_INF : z;
z = x < min_expm1_arg ? -1.0 : z;
return z;
}
#elif __CLC_FPSIZE == 16
_CLC_OVERLOAD _CLC_DEF __CLC_HALFN __clc_expm1(__CLC_HALFN x) {
__CLC_FLOATN x_float = __CLC_CONVERT_FLOATN(x);
__CLC_FLOATN float_expm1 =
__clc_exp2_fast(x_float * (__CLC_FLOATN)M_LOG2E) - 1.0f;
__CLC_HALFN p = __clc_fma(x, x * __clc_fma(x, 0x1.555556p-3h, 0.5h), x);
return __clc_fabs(x) < 0x1.0p-6h ? p : __CLC_CONVERT_HALFN(float_expm1);
}
#endif