| //===----------------------------------------------------------------------===// | |
| // | |
| // 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 |