blob: 89b61870c4e98f0e50f3cb68c0e7095db5d402ad [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
/// Integer-only implementation of expf.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_INTEGER_EVAL_H
#define LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_INTEGER_EVAL_H
#include "hdr/fenv_macros.h"
#include "src/__support/CPP/bit.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/frac64.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h"
#include "src/__support/math/check/exp_exceptions.h"
#include "src/__support/math/exp2f_integer_utils.h"
namespace LIBC_NAMESPACE_DECL {
namespace math {
namespace static_rounding {
// Constants generated by Sollya with:
// > display = hexadecimal;
// > round(log2(exp(1)) * 2^63, 64, RN);
// LSB(LOG2_E) = 2^-63
LIBC_INLINE_VAR constexpr Frac64 LOG2_E = Frac64(0xb8aa'3b29'5c17'f0bc);
LIBC_ALWAYS_INLINE uint32_t expf_bits(uint32_t x_u,
[[maybe_unused]] int rounding) {
using FPBits = fputil::FPBits<float>;
using FPBounds = math::check::exp_internal::Bounds<float>;
bool is_neg = (x_u >> 31) != 0;
uint32_t x_abs = x_u & 0x7fff'ffffU;
uint32_t x_e = x_abs >> FPBits::FRACTION_LEN;
// When |x| < 2^-25 (x_e <= 101) or |x| >= 64 (x_e >= 133):
if (LIBC_UNLIKELY(x_e - 102U >= 31U)) {
// |x| < 2^-25
// exp(x) = 1 + 2^-23 if x > 0 and FE_UPWARD
// = 1 - 2^-24 if x < 0 and FE_DOWNWARD or FE_TOWARDZERO
// = 1 otherwise.
if (x_e <= 101U) {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (x_abs != 0) {
if (rounding == FE_UPWARD && !is_neg)
return 0x3f80'0001U; // 0x1.000002p+0f = 1.0f + 2^-23f
if ((rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO) && is_neg)
return 0x3f7f'ffffU; // 0x1.fffffep-1f = 1.0f - 2^-24f
}
#endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return 0x3f80'0000U; // 1.0f
}
// exp(nan) = nan
if (x_abs > 0x7f80'0000U) // |x| > +inf (x is NaN)
return x_u | 0x0040'0000U; // silence signaling NaN
// x >= log(2^127 * (2 - 2^-23)):
// exp(x) = +inf for FE_TONEAREST, FE_UPWARD
// = FLT_MAX for FE_TOWARDZERO, FE_DOWNWARD
if (!is_neg && x_u >= FPBounds::UPPER_BITS) {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if ((rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO) &&
x_u < 0x7f80'0000U) // x < +inf
return 0x7f7f'ffffU; // 0x1.fffffep+127f (FLT_MAX)
#endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return 0x7f80'0000U; // +inf
}
// x <= -0x1.9fe36ap+6f < log(2^-150)
// exp(x) = 2^-149 for FE_UPWARD
// = 0 otherwise
if (x_u >= 0xc2cf'f1b5U) {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (rounding == FE_UPWARD && x_u < 0xff80'0000U) // x > -inf
return 0x0000'0001U; // 0x1.0p-149f (FLT_TRUE_MIN)
#endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return 0x0000'0000U; // 0.0f
}
}
// Range reduction:
// k = floor(x * log2(e))
// x * log2(e) = k + u, with u in [0, 1)
// e^x = 2^(k + u) = 2^k * 2^u
uint64_t u_bits = exp_b_scale(x_u, x_e, LOG2_E, 133U);
return exp2f_eval_bits(u_bits, is_neg, rounding);
}
// Statically rounded, no-except implementation of expf using integer-only
// arithmetic.
LIBC_ALWAYS_INLINE float expf(float x, [[maybe_unused]] int rounding) {
return cpp::bit_cast<float>(expf_bits(cpp::bit_cast<uint32_t>(x), rounding));
}
} // namespace static_rounding
namespace integer_eval {
LIBC_ALWAYS_INLINE float expf(float x) {
return static_rounding::expf(x, FE_TONEAREST);
}
} // namespace integer_eval
} // namespace math
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXPF_INTEGER_EVAL_H