blob: 1c96050bea37f6ffed0595db820a670e573b86d5 [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
//
//===----------------------------------------------------------------------===//
///
/// \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