| //===----------------------------------------------------------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// Shared utilities and constants for single-precision exponential functions. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_FLOAT_UTILS_H |
| #define LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_FLOAT_UTILS_H |
| |
| #include "src/__support/CPP/bit.h" |
| #include "src/__support/FPUtil/multiply_add.h" |
| #include "src/__support/macros/config.h" |
| #include "src/__support/macros/optimization.h" |
| |
| namespace LIBC_NAMESPACE_DECL { |
| namespace math { |
| namespace float_eval { |
| |
| // Compute y * 2^m for y in [0.5, 2.0) and m in [-150, 128]. |
| // |
| // We divide into three cases depending on m: |
| // 1. -126 <= m <= 127: |
| // 2^m is a normal float (exponent field m + 127 in [1, 254]). |
| // We can directly compute y * 2^m with a single multiplication. |
| // The condition is checked using unsigned comparison: (m + 126) <= 253. |
| // |
| // 2. m > 127 (i.e., m = 128): |
| // 2^m cannot be represented as a finite normal float (exponent field |
| // 128 + 127 = 255 is inf). Instead, we compute: |
| // y * 2^m = (2 * y) * 2^(m - 1). |
| // Since y < 2.0, 2 * y is exact (simply increments the exponent of y), |
| // and 2^(m - 1) = 2^127 is a normal float. |
| // |
| // 3. m < -126 (i.e., m in [-150, -127]): |
| // The result is in the subnormal range (or underflows to 0). |
| // To avoid double-rounding errors, we scale in two steps: |
| // y * 2^m = (y * 2^(m + 25)) * 2^-25. |
| // Since m >= -150, m + 25 >= -125, so 2^(m + 25) is a normal float. |
| // The intermediate product (y * 2^(m + 25)) is normal and computed with |
| // full precision. Then the final multiplication by 2^-25 rounds directly |
| // to the target subnormal precision in a single rounding step. |
| LIBC_INLINE float scale_exp2f(float y, int m) { |
| // -126 <= m <= 127: 2^m is a normal float. |
| if (LIBC_LIKELY(static_cast<uint32_t>(m + 126) <= 253)) { |
| uint32_t scale_bits = static_cast<uint32_t>(m + 127) << 23; |
| float scale = cpp::bit_cast<float>(scale_bits); |
| return y * scale; |
| } |
| |
| // m = 128: 2^m cannot be represented as a normal float. |
| // Compute (2 * y) * 2^(m - 1), where 2 * y is exact and 2^(m - 1) is normal. |
| if (m > 127) { |
| uint32_t scale_bits = static_cast<uint32_t>(m - 1 + 127) << 23; |
| float scale = cpp::bit_cast<float>(scale_bits); |
| return (y * 2.0f) * scale; |
| } |
| |
| // Subnormal result: m in [-150, -127]. |
| // Compute (y * 2^(m + 25)) * 2^-25 so that the first product is normal and |
| // the second multiplication rounds directly to the subnormal result. |
| uint32_t scale_bits = static_cast<uint32_t>(m + 25 + 127) << 23; |
| float scale = cpp::bit_cast<float>(scale_bits); |
| return (y * scale) * 0x1.0p-25f; |
| } |
| |
| // Degree-6 polynomial approximating 2^u - 1 = u * P(u) on [-0.5, 0.5] using |
| // Estrin's scheme and exponent scaling. |
| // Evaluates 2^x = 2^m * 2^u. |
| // Generated by Sollya with: |
| // > display = hexadecimal; |
| // > P = fpminimax((2^x - 1)/x, 5, [|SG...|], [-0.5, 0.5]); |
| // > dirtyinfnorm(1 + x * P - 2^x, [-0.5, 0.5]); |
| // 0x1.68cb...p-28 |
| // > dirtyinfnorm((1 + x * P - 2^x) / 2^x, [-0.5, 0.5]); |
| // 0x1.5109...p-28 |
| LIBC_INLINE float exp2f_eval(float u, int m) { |
| constexpr float COEFFS[6] = {0x1.62e430p-1f, 0x1.ebfbdep-3f, |
| 0x1.c6af9ep-5f, 0x1.3b2c58p-7f, |
| 0x1.5ef4a2p-10f, 0x1.427918p-13f}; |
| |
| float u2 = u * u; |
| float u4 = u2 * u2; |
| |
| float p0 = fputil::multiply_add(u, COEFFS[1], COEFFS[0]); |
| float p1 = fputil::multiply_add(u, COEFFS[3], COEFFS[2]); |
| float p2 = fputil::multiply_add(u, COEFFS[5], COEFFS[4]); |
| |
| float q0 = fputil::multiply_add(u2, p1, p0); |
| float p = fputil::multiply_add(u4, p2, q0); |
| |
| float y = fputil::multiply_add(u, p, 1.0f); |
| |
| return scale_exp2f(y, m); |
| } |
| |
| } // namespace float_eval |
| } // namespace math |
| } // namespace LIBC_NAMESPACE_DECL |
| |
| #endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXP2F_FLOAT_UTILS_H |