123 changes: 2 additions & 121 deletions libc/src/math/generic/exp10f.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,130 +7,11 @@
//===----------------------------------------------------------------------===//

#include "src/math/exp10f.h"
#include "explogxf.h"
#include "src/__support/FPUtil/BasicOperations.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/FPUtil/nearest_integer.h"
#include "src/__support/FPUtil/rounding_mode.h"
#include "src/__support/common.h"
#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY

#include <errno.h>
#include "src/math/generic/exp10f_impl.h"

namespace LIBC_NAMESPACE {

LLVM_LIBC_FUNCTION(float, exp10f, (float x)) {
using FPBits = typename fputil::FPBits<float>;
FPBits xbits(x);

uint32_t x_u = xbits.uintval();
uint32_t x_abs = x_u & 0x7fff'ffffU;

// When |x| >= log10(2^128), or x is nan
if (LIBC_UNLIKELY(x_abs >= 0x421a'209bU)) {
// When x < log10(2^-150) or nan
if (x_u > 0xc234'9e35U) {
// exp(-Inf) = 0
if (xbits.is_inf())
return 0.0f;
// exp(nan) = nan
if (xbits.is_nan())
return x;
if (fputil::fenv_is_round_up())
return static_cast<float>(FPBits(FPBits::MIN_SUBNORMAL));
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_UNDERFLOW);
return 0.0f;
}
// x >= log10(2^128) or nan
if (!xbits.get_sign() && (x_u >= 0x421a'209bU)) {
// x is finite
if (x_u < 0x7f80'0000U) {
int rounding = fputil::quick_get_round();
if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
return static_cast<float>(FPBits(FPBits::MAX_NORMAL));

fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);
}
// x is +inf or nan
return x + static_cast<float>(FPBits::inf());
}
}

// When |x| <= log10(2)*2^-6
if (LIBC_UNLIKELY(x_abs <= 0x3b9a'209bU)) {
if (LIBC_UNLIKELY(x_u == 0xb25e'5bd9U)) { // x = -0x1.bcb7b2p-27f
if (fputil::fenv_is_round_to_nearest())
return 0x1.fffffep-1f;
}
// |x| < 2^-25
// 10^x ~ 1 + log(10) * x
if (LIBC_UNLIKELY(x_abs <= 0x3280'0000U)) {
return fputil::multiply_add(x, 0x1.26bb1cp+1f, 1.0f);
}

return static_cast<float>(Exp10Base::powb_lo(x));
}

// Exceptional value.
if (LIBC_UNLIKELY(x_u == 0x3d14'd956U)) { // x = 0x1.29b2acp-5f
if (fputil::fenv_is_round_up())
return 0x1.1657c4p+0f;
}

// Exact outputs when x = 1, 2, ..., 10.
// Quick check mask: 0x800f'ffffU = ~(bits of 1.0f | ... | bits of 10.0f)
if (LIBC_UNLIKELY((x_u & 0x800f'ffffU) == 0)) {
switch (x_u) {
case 0x3f800000U: // x = 1.0f
return 10.0f;
case 0x40000000U: // x = 2.0f
return 100.0f;
case 0x40400000U: // x = 3.0f
return 1'000.0f;
case 0x40800000U: // x = 4.0f
return 10'000.0f;
case 0x40a00000U: // x = 5.0f
return 100'000.0f;
case 0x40c00000U: // x = 6.0f
return 1'000'000.0f;
case 0x40e00000U: // x = 7.0f
return 10'000'000.0f;
case 0x41000000U: // x = 8.0f
return 100'000'000.0f;
case 0x41100000U: // x = 9.0f
return 1'000'000'000.0f;
case 0x41200000U: // x = 10.0f
return 10'000'000'000.0f;
}
}

// Range reduction: 10^x = 2^(mid + hi) * 10^lo
// rr = (2^(mid + hi), lo)
auto rr = exp_b_range_reduc<Exp10Base>(x);

// The low part is approximated by a degree-5 minimax polynomial.
// 10^lo ~ 1 + COEFFS[0] * lo + ... + COEFFS[4] * lo^5
using fputil::multiply_add;
double lo2 = rr.lo * rr.lo;
// c0 = 1 + COEFFS[0] * lo
double c0 = multiply_add(rr.lo, Exp10Base::COEFFS[0], 1.0);
// c1 = COEFFS[1] + COEFFS[2] * lo
double c1 = multiply_add(rr.lo, Exp10Base::COEFFS[2], Exp10Base::COEFFS[1]);
// c2 = COEFFS[3] + COEFFS[4] * lo
double c2 = multiply_add(rr.lo, Exp10Base::COEFFS[4], Exp10Base::COEFFS[3]);
// p = c1 + c2 * lo^2
// = COEFFS[1] + COEFFS[2] * lo + COEFFS[3] * lo^2 + COEFFS[4] * lo^3
double p = multiply_add(lo2, c2, c1);
// 10^lo ~ c0 + p * lo^2
// 10^x = 2^(mid + hi) * 10^lo
// ~ mh * (c0 + p * lo^2)
// = (mh * c0) + p * (mh * lo^2)
return static_cast<float>(multiply_add(p, lo2 * rr.mh, c0 * rr.mh));
}
LLVM_LIBC_FUNCTION(float, exp10f, (float x)) { return generic::exp10f(x); }

} // namespace LIBC_NAMESPACE
141 changes: 141 additions & 0 deletions libc/src/math/generic/exp10f_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
//===-- Single-precision 10^x function ------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_MATH_GENERIC_EXP10F_IMPL_H
#define LLVM_LIBC_SRC_MATH_GENERIC_EXP10F_IMPL_H

#include "explogxf.h"
#include "src/__support/FPUtil/BasicOperations.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/FPUtil/nearest_integer.h"
#include "src/__support/FPUtil/rounding_mode.h"
#include "src/__support/common.h"
#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
#include "src/math/exp10f.h"

#include <errno.h>

namespace LIBC_NAMESPACE::generic {

LIBC_INLINE float exp10f(float x) {
using FPBits = typename fputil::FPBits<float>;
FPBits xbits(x);

uint32_t x_u = xbits.uintval();
uint32_t x_abs = x_u & 0x7fff'ffffU;

// When |x| >= log10(2^128), or x is nan
if (LIBC_UNLIKELY(x_abs >= 0x421a'209bU)) {
// When x < log10(2^-150) or nan
if (x_u > 0xc234'9e35U) {
// exp(-Inf) = 0
if (xbits.is_inf())
return 0.0f;
// exp(nan) = nan
if (xbits.is_nan())
return x;
if (fputil::fenv_is_round_up())
return static_cast<float>(FPBits(FPBits::MIN_SUBNORMAL));
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_UNDERFLOW);
return 0.0f;
}
// x >= log10(2^128) or nan
if (!xbits.get_sign() && (x_u >= 0x421a'209bU)) {
// x is finite
if (x_u < 0x7f80'0000U) {
int rounding = fputil::quick_get_round();
if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
return static_cast<float>(FPBits(FPBits::MAX_NORMAL));

fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);
}
// x is +inf or nan
return x + static_cast<float>(FPBits::inf());
}
}

// When |x| <= log10(2)*2^-6
if (LIBC_UNLIKELY(x_abs <= 0x3b9a'209bU)) {
if (LIBC_UNLIKELY(x_u == 0xb25e'5bd9U)) { // x = -0x1.bcb7b2p-27f
if (fputil::fenv_is_round_to_nearest())
return 0x1.fffffep-1f;
}
// |x| < 2^-25
// 10^x ~ 1 + log(10) * x
if (LIBC_UNLIKELY(x_abs <= 0x3280'0000U)) {
return fputil::multiply_add(x, 0x1.26bb1cp+1f, 1.0f);
}

return static_cast<float>(Exp10Base::powb_lo(x));
}

// Exceptional value.
if (LIBC_UNLIKELY(x_u == 0x3d14'd956U)) { // x = 0x1.29b2acp-5f
if (fputil::fenv_is_round_up())
return 0x1.1657c4p+0f;
}

// Exact outputs when x = 1, 2, ..., 10.
// Quick check mask: 0x800f'ffffU = ~(bits of 1.0f | ... | bits of 10.0f)
if (LIBC_UNLIKELY((x_u & 0x800f'ffffU) == 0)) {
switch (x_u) {
case 0x3f800000U: // x = 1.0f
return 10.0f;
case 0x40000000U: // x = 2.0f
return 100.0f;
case 0x40400000U: // x = 3.0f
return 1'000.0f;
case 0x40800000U: // x = 4.0f
return 10'000.0f;
case 0x40a00000U: // x = 5.0f
return 100'000.0f;
case 0x40c00000U: // x = 6.0f
return 1'000'000.0f;
case 0x40e00000U: // x = 7.0f
return 10'000'000.0f;
case 0x41000000U: // x = 8.0f
return 100'000'000.0f;
case 0x41100000U: // x = 9.0f
return 1'000'000'000.0f;
case 0x41200000U: // x = 10.0f
return 10'000'000'000.0f;
}
}

// Range reduction: 10^x = 2^(mid + hi) * 10^lo
// rr = (2^(mid + hi), lo)
auto rr = exp_b_range_reduc<Exp10Base>(x);

// The low part is approximated by a degree-5 minimax polynomial.
// 10^lo ~ 1 + COEFFS[0] * lo + ... + COEFFS[4] * lo^5
using fputil::multiply_add;
double lo2 = rr.lo * rr.lo;
// c0 = 1 + COEFFS[0] * lo
double c0 = multiply_add(rr.lo, Exp10Base::COEFFS[0], 1.0);
// c1 = COEFFS[1] + COEFFS[2] * lo
double c1 = multiply_add(rr.lo, Exp10Base::COEFFS[2], Exp10Base::COEFFS[1]);
// c2 = COEFFS[3] + COEFFS[4] * lo
double c2 = multiply_add(rr.lo, Exp10Base::COEFFS[4], Exp10Base::COEFFS[3]);
// p = c1 + c2 * lo^2
// = COEFFS[1] + COEFFS[2] * lo + COEFFS[3] * lo^2 + COEFFS[4] * lo^3
double p = multiply_add(lo2, c2, c1);
// 10^lo ~ c0 + p * lo^2
// 10^x = 2^(mid + hi) * 10^lo
// ~ mh * (c0 + p * lo^2)
// = (mh * c0) + p * (mh * lo^2)
return static_cast<float>(multiply_add(p, lo2 * rr.mh, c0 * rr.mh));
}

} // namespace LIBC_NAMESPACE::generic

#endif // LLVM_LIBC_SRC_MATH_GENERIC_EXP10F_IMPL_H
150 changes: 3 additions & 147 deletions libc/src/math/generic/exp2f.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,155 +7,11 @@
//===----------------------------------------------------------------------===//

#include "src/math/exp2f.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/except_value_utils.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/FPUtil/nearest_integer.h"
#include "src/__support/FPUtil/rounding_mode.h"
#include "src/__support/common.h"
#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
#include "src/__support/macros/properties/cpu_features.h"

#include <errno.h>

#include "explogxf.h"
#include "src/__support/common.h" // for LLVM_LIBC_FUNCTION
#include "src/math/generic/exp2f_impl.h"

namespace LIBC_NAMESPACE {

constexpr uint32_t EXVAL1 = 0x3b42'9d37U;
constexpr uint32_t EXVAL2 = 0xbcf3'a937U;
constexpr uint32_t EXVAL_MASK = EXVAL1 & EXVAL2;

LLVM_LIBC_FUNCTION(float, exp2f, (float x)) {
using FPBits = typename fputil::FPBits<float>;
FPBits xbits(x);

uint32_t x_u = xbits.uintval();
uint32_t x_abs = x_u & 0x7fff'ffffU;

// When |x| >= 128, or x is nan, or |x| <= 2^-5
if (LIBC_UNLIKELY(x_abs >= 0x4300'0000U || x_abs <= 0x3d00'0000U)) {
// |x| <= 2^-5
if (x_abs <= 0x3d00'0000) {
// |x| < 2^-25
if (LIBC_UNLIKELY(x_abs <= 0x3280'0000U)) {
return 1.0f + x;
}

// Check exceptional values.
if (LIBC_UNLIKELY((x_u & EXVAL_MASK) == EXVAL_MASK)) {
if (LIBC_UNLIKELY(x_u == EXVAL1)) { // x = 0x1.853a6ep-9f
return fputil::round_result_slightly_down(0x1.00870ap+0f);
} else if (LIBC_UNLIKELY(x_u == EXVAL2)) { // x = -0x1.e7526ep-6f
return fputil::round_result_slightly_down(0x1.f58d62p-1f);
}
}

// Minimax polynomial generated by Sollya with:
// > P = fpminimax((2^x - 1)/x, 5, [|D...|], [-2^-5, 2^-5]);
constexpr double COEFFS[] = {
0x1.62e42fefa39f3p-1, 0x1.ebfbdff82c57bp-3, 0x1.c6b08d6f2d7aap-5,
0x1.3b2ab6fc92f5dp-7, 0x1.5d897cfe27125p-10, 0x1.43090e61e6af1p-13};
double xd = static_cast<double>(x);
double xsq = xd * xd;
double c0 = fputil::multiply_add(xd, COEFFS[1], COEFFS[0]);
double c1 = fputil::multiply_add(xd, COEFFS[3], COEFFS[2]);
double c2 = fputil::multiply_add(xd, COEFFS[5], COEFFS[4]);
double p = fputil::polyeval(xsq, c0, c1, c2);
double r = fputil::multiply_add(p, xd, 1.0);
return static_cast<float>(r);
}

// x >= 128
if (!xbits.get_sign()) {
// x is finite
if (x_u < 0x7f80'0000U) {
int rounding = fputil::quick_get_round();
if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
return static_cast<float>(FPBits(FPBits::MAX_NORMAL));

fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);
}
// x is +inf or nan
return x + FPBits::inf().get_val();
}
// x <= -150
if (x_u >= 0xc316'0000U) {
// exp(-Inf) = 0
if (xbits.is_inf())
return 0.0f;
// exp(nan) = nan
if (xbits.is_nan())
return x;
if (fputil::fenv_is_round_up())
return FPBits(FPBits::MIN_SUBNORMAL).get_val();
if (x != 0.0f) {
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_UNDERFLOW);
}
return 0.0f;
}
}

// For -150 < x < 128, to compute 2^x, we perform the following range
// reduction: find hi, mid, lo such that:
// x = hi + mid + lo, in which
// hi is an integer,
// 0 <= mid * 2^5 < 32 is an integer
// -2^(-6) <= lo <= 2^-6.
// In particular,
// hi + mid = round(x * 2^5) * 2^(-5).
// Then,
// 2^x = 2^(hi + mid + lo) = 2^hi * 2^mid * 2^lo.
// 2^mid is stored in the lookup table of 32 elements.
// 2^lo is computed using a degree-5 minimax polynomial
// generated by Sollya.
// We perform 2^hi * 2^mid by simply add hi to the exponent field
// of 2^mid.

// kf = (hi + mid) * 2^5 = round(x * 2^5)
float kf;
int k;
#ifdef LIBC_TARGET_CPU_HAS_NEAREST_INT
kf = fputil::nearest_integer(x * 32.0f);
k = static_cast<int>(kf);
#else
constexpr float HALF[2] = {0.5f, -0.5f};
k = static_cast<int>(fputil::multiply_add(x, 32.0f, HALF[x < 0.0f]));
kf = static_cast<float>(k);
#endif // LIBC_TARGET_CPU_HAS_NEAREST_INT

// dx = lo = x - (hi + mid) = x - kf * 2^(-5)
double dx = fputil::multiply_add(-0x1.0p-5f, kf, x);

// hi = floor(kf * 2^(-4))
// exp_hi = shift hi to the exponent field of double precision.
int64_t exp_hi =
static_cast<int64_t>(static_cast<uint64_t>(k >> ExpBase::MID_BITS)
<< fputil::FloatProperties<double>::MANTISSA_WIDTH);
// mh = 2^hi * 2^mid
// mh_bits = bit field of mh
int64_t mh_bits = ExpBase::EXP_2_MID[k & ExpBase::MID_MASK] + exp_hi;
double mh = fputil::FPBits<double>(uint64_t(mh_bits)).get_val();

// Degree-5 polynomial approximating (2^x - 1)/x generating by Sollya with:
// > P = fpminimax((2^x - 1)/x, 5, [|D...|], [-1/32. 1/32]);
constexpr double COEFFS[5] = {0x1.62e42fefa39efp-1, 0x1.ebfbdff8131c4p-3,
0x1.c6b08d7061695p-5, 0x1.3b2b1bee74b2ap-7,
0x1.5d88091198529p-10};
double dx_sq = dx * dx;
double c1 = fputil::multiply_add(dx, COEFFS[0], 1.0);
double c2 = fputil::multiply_add(dx, COEFFS[2], COEFFS[1]);
double c3 = fputil::multiply_add(dx, COEFFS[4], COEFFS[3]);
double p = fputil::multiply_add(dx_sq, c3, c2);
// 2^x = 2^(hi + mid + lo)
// = 2^(hi + mid) * 2^lo
// ~ mh * (1 + lo * P(lo))
// = mh + (mh*lo) * P(lo)
return static_cast<float>(fputil::multiply_add(p, dx_sq * mh, c1 * mh));
}
LLVM_LIBC_FUNCTION(float, exp2f, (float x)) { return generic::exp2f(x); }

} // namespace LIBC_NAMESPACE
165 changes: 165 additions & 0 deletions libc/src/math/generic/exp2f_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
//===-- Single-precision 2^x function -------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_MATH_GENERIC_EXP2F_IMPL_H
#define LLVM_LIBC_SRC_MATH_GENERIC_EXP2F_IMPL_H

#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/except_value_utils.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/FPUtil/nearest_integer.h"
#include "src/__support/FPUtil/rounding_mode.h"
#include "src/__support/common.h"
#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
#include "src/__support/macros/properties/cpu_features.h"

#include <errno.h>

#include "explogxf.h"

namespace LIBC_NAMESPACE::generic {

LIBC_INLINE float exp2f(float x) {
constexpr uint32_t EXVAL1 = 0x3b42'9d37U;
constexpr uint32_t EXVAL2 = 0xbcf3'a937U;
constexpr uint32_t EXVAL_MASK = EXVAL1 & EXVAL2;

using FPBits = typename fputil::FPBits<float>;
FPBits xbits(x);

uint32_t x_u = xbits.uintval();
uint32_t x_abs = x_u & 0x7fff'ffffU;

// When |x| >= 128, or x is nan, or |x| <= 2^-5
if (LIBC_UNLIKELY(x_abs >= 0x4300'0000U || x_abs <= 0x3d00'0000U)) {
// |x| <= 2^-5
if (x_abs <= 0x3d00'0000) {
// |x| < 2^-25
if (LIBC_UNLIKELY(x_abs <= 0x3280'0000U)) {
return 1.0f + x;
}

// Check exceptional values.
if (LIBC_UNLIKELY((x_u & EXVAL_MASK) == EXVAL_MASK)) {
if (LIBC_UNLIKELY(x_u == EXVAL1)) { // x = 0x1.853a6ep-9f
return fputil::round_result_slightly_down(0x1.00870ap+0f);
} else if (LIBC_UNLIKELY(x_u == EXVAL2)) { // x = -0x1.e7526ep-6f
return fputil::round_result_slightly_down(0x1.f58d62p-1f);
}
}

// Minimax polynomial generated by Sollya with:
// > P = fpminimax((2^x - 1)/x, 5, [|D...|], [-2^-5, 2^-5]);
constexpr double COEFFS[] = {
0x1.62e42fefa39f3p-1, 0x1.ebfbdff82c57bp-3, 0x1.c6b08d6f2d7aap-5,
0x1.3b2ab6fc92f5dp-7, 0x1.5d897cfe27125p-10, 0x1.43090e61e6af1p-13};
double xd = static_cast<double>(x);
double xsq = xd * xd;
double c0 = fputil::multiply_add(xd, COEFFS[1], COEFFS[0]);
double c1 = fputil::multiply_add(xd, COEFFS[3], COEFFS[2]);
double c2 = fputil::multiply_add(xd, COEFFS[5], COEFFS[4]);
double p = fputil::polyeval(xsq, c0, c1, c2);
double r = fputil::multiply_add(p, xd, 1.0);
return static_cast<float>(r);
}

// x >= 128
if (!xbits.get_sign()) {
// x is finite
if (x_u < 0x7f80'0000U) {
int rounding = fputil::quick_get_round();
if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
return static_cast<float>(FPBits(FPBits::MAX_NORMAL));

fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);
}
// x is +inf or nan
return x + FPBits::inf().get_val();
}
// x <= -150
if (x_u >= 0xc316'0000U) {
// exp(-Inf) = 0
if (xbits.is_inf())
return 0.0f;
// exp(nan) = nan
if (xbits.is_nan())
return x;
if (fputil::fenv_is_round_up())
return FPBits(FPBits::MIN_SUBNORMAL).get_val();
if (x != 0.0f) {
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_UNDERFLOW);
}
return 0.0f;
}
}

// For -150 < x < 128, to compute 2^x, we perform the following range
// reduction: find hi, mid, lo such that:
// x = hi + mid + lo, in which
// hi is an integer,
// 0 <= mid * 2^5 < 32 is an integer
// -2^(-6) <= lo <= 2^-6.
// In particular,
// hi + mid = round(x * 2^5) * 2^(-5).
// Then,
// 2^x = 2^(hi + mid + lo) = 2^hi * 2^mid * 2^lo.
// 2^mid is stored in the lookup table of 32 elements.
// 2^lo is computed using a degree-5 minimax polynomial
// generated by Sollya.
// We perform 2^hi * 2^mid by simply add hi to the exponent field
// of 2^mid.

// kf = (hi + mid) * 2^5 = round(x * 2^5)
float kf;
int k;
#ifdef LIBC_TARGET_CPU_HAS_NEAREST_INT
kf = fputil::nearest_integer(x * 32.0f);
k = static_cast<int>(kf);
#else
constexpr float HALF[2] = {0.5f, -0.5f};
k = static_cast<int>(fputil::multiply_add(x, 32.0f, HALF[x < 0.0f]));
kf = static_cast<float>(k);
#endif // LIBC_TARGET_CPU_HAS_NEAREST_INT

// dx = lo = x - (hi + mid) = x - kf * 2^(-5)
double dx = fputil::multiply_add(-0x1.0p-5f, kf, x);

// hi = floor(kf * 2^(-4))
// exp_hi = shift hi to the exponent field of double precision.
int64_t exp_hi =
static_cast<int64_t>(static_cast<uint64_t>(k >> ExpBase::MID_BITS)
<< fputil::FloatProperties<double>::MANTISSA_WIDTH);
// mh = 2^hi * 2^mid
// mh_bits = bit field of mh
int64_t mh_bits = ExpBase::EXP_2_MID[k & ExpBase::MID_MASK] + exp_hi;
double mh = fputil::FPBits<double>(uint64_t(mh_bits)).get_val();

// Degree-5 polynomial approximating (2^x - 1)/x generating by Sollya with:
// > P = fpminimax((2^x - 1)/x, 5, [|D...|], [-1/32. 1/32]);
constexpr double COEFFS[5] = {0x1.62e42fefa39efp-1, 0x1.ebfbdff8131c4p-3,
0x1.c6b08d7061695p-5, 0x1.3b2b1bee74b2ap-7,
0x1.5d88091198529p-10};
double dx_sq = dx * dx;
double c1 = fputil::multiply_add(dx, COEFFS[0], 1.0);
double c2 = fputil::multiply_add(dx, COEFFS[2], COEFFS[1]);
double c3 = fputil::multiply_add(dx, COEFFS[4], COEFFS[3]);
double p = fputil::multiply_add(dx_sq, c3, c2);
// 2^x = 2^(hi + mid + lo)
// = 2^(hi + mid) * 2^lo
// ~ mh * (1 + lo * P(lo))
// = mh + (mh*lo) * P(lo)
return static_cast<float>(fputil::multiply_add(p, dx_sq * mh, c1 * mh));
}

} // namespace LIBC_NAMESPACE::generic

#endif // LLVM_LIBC_SRC_MATH_GENERIC_EXP2F_IMPL_H
46 changes: 0 additions & 46 deletions libc/src/math/generic/log2f.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,52 +53,6 @@

namespace LIBC_NAMESPACE {

// Lookup table for log2(f) = log2(1 + n*2^(-7)) where n = 0..127.
static constexpr double LOG2_R[128] = {
0x0.0000000000000p+0, 0x1.72c7ba20f7327p-7, 0x1.743ee861f3556p-6,
0x1.184b8e4c56af8p-5, 0x1.77394c9d958d5p-5, 0x1.d6ebd1f1febfep-5,
0x1.1bb32a600549dp-4, 0x1.4c560fe68af88p-4, 0x1.7d60496cfbb4cp-4,
0x1.960caf9abb7cap-4, 0x1.c7b528b70f1c5p-4, 0x1.f9c95dc1d1165p-4,
0x1.097e38ce60649p-3, 0x1.22dadc2ab3497p-3, 0x1.3c6fb650cde51p-3,
0x1.494f863b8df35p-3, 0x1.633a8bf437ce1p-3, 0x1.7046031c79f85p-3,
0x1.8a8980abfbd32p-3, 0x1.97c1cb13c7ec1p-3, 0x1.b2602497d5346p-3,
0x1.bfc67a7fff4ccp-3, 0x1.dac22d3e441d3p-3, 0x1.e857d3d361368p-3,
0x1.01d9bbcfa61d4p-2, 0x1.08bce0d95fa38p-2, 0x1.169c05363f158p-2,
0x1.1d982c9d52708p-2, 0x1.249cd2b13cd6cp-2, 0x1.32bfee370ee68p-2,
0x1.39de8e1559f6fp-2, 0x1.4106017c3eca3p-2, 0x1.4f6fbb2cec598p-2,
0x1.56b22e6b578e5p-2, 0x1.5dfdcf1eeae0ep-2, 0x1.6552b49986277p-2,
0x1.6cb0f6865c8eap-2, 0x1.7b89f02cf2aadp-2, 0x1.8304d90c11fd3p-2,
0x1.8a8980abfbd32p-2, 0x1.921800924dd3bp-2, 0x1.99b072a96c6b2p-2,
0x1.a8ff971810a5ep-2, 0x1.b0b67f4f4681p-2, 0x1.b877c57b1b07p-2,
0x1.c043859e2fdb3p-2, 0x1.c819dc2d45fe4p-2, 0x1.cffae611ad12bp-2,
0x1.d7e6c0abc3579p-2, 0x1.dfdd89d586e2bp-2, 0x1.e7df5fe538ab3p-2,
0x1.efec61b011f85p-2, 0x1.f804ae8d0cd02p-2, 0x1.0014332be0033p-1,
0x1.042bd4b9a7c99p-1, 0x1.08494c66b8efp-1, 0x1.0c6caaf0c5597p-1,
0x1.1096015dee4dap-1, 0x1.14c560fe68af9p-1, 0x1.18fadb6e2d3c2p-1,
0x1.1d368296b5255p-1, 0x1.217868b0c37e8p-1, 0x1.25c0a0463bebp-1,
0x1.2a0f3c340705cp-1, 0x1.2e644fac04fd8p-1, 0x1.2e644fac04fd8p-1,
0x1.32bfee370ee68p-1, 0x1.37222bb70747cp-1, 0x1.3b8b1c68fa6edp-1,
0x1.3ffad4e74f1d6p-1, 0x1.44716a2c08262p-1, 0x1.44716a2c08262p-1,
0x1.48eef19317991p-1, 0x1.4d7380dcc422dp-1, 0x1.51ff2e30214bcp-1,
0x1.5692101d9b4a6p-1, 0x1.5b2c3da19723bp-1, 0x1.5b2c3da19723bp-1,
0x1.5fcdce2727ddbp-1, 0x1.6476d98ad990ap-1, 0x1.6927781d932a8p-1,
0x1.6927781d932a8p-1, 0x1.6ddfc2a78fc63p-1, 0x1.729fd26b707c8p-1,
0x1.7767c12967a45p-1, 0x1.7767c12967a45p-1, 0x1.7c37a9227e7fbp-1,
0x1.810fa51bf65fdp-1, 0x1.810fa51bf65fdp-1, 0x1.85efd062c656dp-1,
0x1.8ad846cf369a4p-1, 0x1.8ad846cf369a4p-1, 0x1.8fc924c89ac84p-1,
0x1.94c287492c4dbp-1, 0x1.94c287492c4dbp-1, 0x1.99c48be2063c8p-1,
0x1.9ecf50bf43f13p-1, 0x1.9ecf50bf43f13p-1, 0x1.a3e2f4ac43f6p-1,
0x1.a8ff971810a5ep-1, 0x1.a8ff971810a5ep-1, 0x1.ae255819f022dp-1,
0x1.b35458761d479p-1, 0x1.b35458761d479p-1, 0x1.b88cb9a2ab521p-1,
0x1.b88cb9a2ab521p-1, 0x1.bdce9dcc96187p-1, 0x1.c31a27dd00b4ap-1,
0x1.c31a27dd00b4ap-1, 0x1.c86f7b7ea4a89p-1, 0x1.c86f7b7ea4a89p-1,
0x1.cdcebd2373995p-1, 0x1.d338120a6dd9dp-1, 0x1.d338120a6dd9dp-1,
0x1.d8aba045b01c8p-1, 0x1.d8aba045b01c8p-1, 0x1.de298ec0bac0dp-1,
0x1.de298ec0bac0dp-1, 0x1.e3b20546f554ap-1, 0x1.e3b20546f554ap-1,
0x1.e9452c8a71028p-1, 0x1.e9452c8a71028p-1, 0x1.eee32e2aeccbfp-1,
0x1.eee32e2aeccbfp-1, 0x1.f48c34bd1e96fp-1, 0x1.f48c34bd1e96fp-1,
0x1.fa406bd2443dfp-1, 0x1.0000000000000p0};

LLVM_LIBC_FUNCTION(float, log2f, (float x)) {
using FPBits = typename fputil::FPBits<float>;
FPBits xbits(x);
Expand Down
841 changes: 841 additions & 0 deletions libc/src/math/generic/powf.cpp

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions libc/test/UnitTest/FPMatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ template <typename T> struct FPTest : public Test {
static constexpr T aNaN = T(FPBits::build_quiet_nan(1));
static constexpr T inf = T(FPBits::inf());
static constexpr T neg_inf = T(FPBits::neg_inf());
static constexpr int N_ROUNDING_MODES = 4;
static constexpr fputil::testing::RoundingMode ROUNDING_MODES[4] = {
fputil::testing::RoundingMode::Nearest,
fputil::testing::RoundingMode::Upward,
fputil::testing::RoundingMode::Downward,
fputil::testing::RoundingMode::TowardZero,
};
};

} // namespace testing
Expand Down
13 changes: 13 additions & 0 deletions libc/test/src/math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1696,6 +1696,19 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
powf_test
NEED_MPFR
SUITE
libc-math-unittests
SRCS
powf_test.cpp
DEPENDS
libc.include.math
libc.src.math.powf
libc.src.__support.FPUtil.fp_bits
)

add_subdirectory(generic)
add_subdirectory(smoke)

Expand Down
123 changes: 123 additions & 0 deletions libc/test/src/math/powf_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//===-- Unittests for powf ------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#include "src/__support/FPUtil/FPBits.h"
#include "src/math/powf.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
#include "utils/MPFRWrapper/MPFRUtils.h"
#include <math.h>

#include <errno.h>
#include <stdint.h>

using LlvmLibcPowfTest = LIBC_NAMESPACE::testing::FPTest<float>;
using LIBC_NAMESPACE::testing::tlog;

namespace mpfr = LIBC_NAMESPACE::testing::mpfr;

TEST_F(LlvmLibcPowfTest, TrickyInputs) {
constexpr int N = 11;
constexpr mpfr::BinaryInput<float> INPUTS[N] = {
{0x1.290bbp-124f, 0x1.1e6d92p-25f}, {0x1.2e9fb6p+5f, -0x1.1b82b6p-18f},
{0x1.6877f6p+60f, -0x1.75f1c6p-4f}, {0x1.0936acp-63f, -0x1.55200ep-15f},
{0x1.d6d72ap+43f, -0x1.749ccap-5f}, {0x1.4afb2ap-40f, 0x1.063198p+0f},
{0x1.0124dep+0f, -0x1.fdb016p+9f}, {0x1.1058p+0f, 0x1.ap+64f},
{0x1.1058p+0f, -0x1.ap+64f}, {0x1.1058p+0f, 0x1.ap+64f},
{0x1.fa32d4p-1f, 0x1.67a62ep+12f},
};

for (int i = 0; i < N; ++i) {
float x = INPUTS[i].x;
float y = INPUTS[i].y;
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Pow, INPUTS[i],
LIBC_NAMESPACE::powf(x, y), 0.5);
}
}

TEST_F(LlvmLibcPowfTest, InFloatRange) {
constexpr uint32_t X_COUNT = 1'23;
constexpr uint32_t X_START = FPBits(0.25f).uintval();
constexpr uint32_t X_STOP = FPBits(4.0f).uintval();
constexpr uint32_t X_STEP = (X_STOP - X_START) / X_COUNT;

constexpr uint32_t Y_COUNT = 1'37;
constexpr uint32_t Y_START = FPBits(0.25f).uintval();
constexpr uint32_t Y_STOP = FPBits(4.0f).uintval();
constexpr uint32_t Y_STEP = (Y_STOP - Y_START) / Y_COUNT;

auto test = [&](mpfr::RoundingMode rounding_mode) {
mpfr::ForceRoundingMode __r(rounding_mode);
if (!__r.success)
return;

uint64_t fails = 0;
uint64_t count = 0;
uint64_t cc = 0;
float mx, my, mr = 0.0;
double tol = 0.5;

for (uint32_t i = 0, v = X_START; i <= X_COUNT; ++i, v += X_STEP) {
float x = FPBits(v).get_val();
if (isnan(x) || isinf(x) || x < 0.0)
continue;

for (uint32_t j = 0, w = Y_START; j <= Y_COUNT; ++j, w += Y_STEP) {
float y = FPBits(w).get_val();
if (isnan(y) || isinf(y))
continue;

libc_errno = 0;
float result = LIBC_NAMESPACE::powf(x, y);
++cc;
if (isnan(result) || isinf(result))
continue;

++count;
mpfr::BinaryInput<float> inputs{x, y};

if (!TEST_MPFR_MATCH_ROUNDING_SILENTLY(mpfr::Operation::Pow, inputs,
result, 0.5, rounding_mode)) {
++fails;
while (!TEST_MPFR_MATCH_ROUNDING_SILENTLY(
mpfr::Operation::Pow, inputs, result, tol, rounding_mode)) {
mx = x;
my = y;
mr = result;

if (tol > 1000.0)
break;

tol *= 2.0;
}
}
}
}
if (fails || (count < cc)) {
tlog << " Powf failed: " << fails << "/" << count << "/" << cc
<< " tests.\n"
<< " Max ULPs is at most: " << static_cast<uint64_t>(tol) << ".\n";
}
if (fails) {
mpfr::BinaryInput<float> inputs{mx, my};
EXPECT_MPFR_MATCH(mpfr::Operation::Pow, inputs, mr, 0.5, rounding_mode);
}
};

tlog << " Test Rounding To Nearest...\n";
test(mpfr::RoundingMode::Nearest);

tlog << " Test Rounding Downward...\n";
test(mpfr::RoundingMode::Downward);

tlog << " Test Rounding Upward...\n";
test(mpfr::RoundingMode::Upward);

tlog << " Test Rounding Toward Zero...\n";
test(mpfr::RoundingMode::TowardZero);
}
12 changes: 12 additions & 0 deletions libc/test/src/math/smoke/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1534,3 +1534,15 @@ add_fp_unittest(
libc.src.math.erff
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
powf_test
SUITE
libc-math-smoke-tests
SRCS
powf_test.cpp
DEPENDS
libc.include.math
libc.src.math.powf
libc.src.__support.FPUtil.fp_bits
)
189 changes: 189 additions & 0 deletions libc/test/src/math/smoke/powf_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
//===-- Unittests for powf ------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#include "src/__support/FPUtil/FPBits.h"
#include "src/math/powf.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
#include <math.h>

#include <errno.h>
#include <stdint.h>

using LlvmLibcPowfTest = LIBC_NAMESPACE::testing::FPTest<float>;
using LIBC_NAMESPACE::fputil::testing::ForceRoundingMode;
using LIBC_NAMESPACE::fputil::testing::RoundingMode;

TEST_F(LlvmLibcPowfTest, SpecialNumbers) {
constexpr float neg_odd_integer = -3.0f;
constexpr float neg_even_integer = -6.0f;
constexpr float neg_non_integer = -1.1f;
constexpr float pos_odd_integer = 5.0f;
constexpr float pos_even_integer = 8.0f;
constexpr float pos_non_integer = 1.1f;

for (int i = 0; i < N_ROUNDING_MODES; ++i) {
ForceRoundingMode __r(ROUNDING_MODES[i]);
if (!__r.success)
continue;

// pow( 0.0f, exponent )
EXPECT_FP_EQ_WITH_EXCEPTION(
inf, LIBC_NAMESPACE::powf(zero, neg_odd_integer), FE_DIVBYZERO);
EXPECT_FP_EQ_WITH_EXCEPTION(
inf, LIBC_NAMESPACE::powf(zero, neg_even_integer), FE_DIVBYZERO);
EXPECT_FP_EQ_WITH_EXCEPTION(
inf, LIBC_NAMESPACE::powf(zero, neg_non_integer), FE_DIVBYZERO);
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(zero, pos_odd_integer));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(zero, pos_even_integer));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(zero, pos_non_integer));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(zero, zero));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(zero, neg_zero));
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::powf(zero, inf));
EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::powf(zero, neg_inf),
FE_DIVBYZERO);
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::powf(zero, aNaN));

// pow( -0.0f, exponent )
EXPECT_FP_EQ_WITH_EXCEPTION(
neg_inf, LIBC_NAMESPACE::powf(neg_zero, neg_odd_integer), FE_DIVBYZERO);
EXPECT_FP_EQ_WITH_EXCEPTION(
inf, LIBC_NAMESPACE::powf(neg_zero, neg_even_integer), FE_DIVBYZERO);
EXPECT_FP_EQ_WITH_EXCEPTION(
inf, LIBC_NAMESPACE::powf(neg_zero, neg_non_integer), FE_DIVBYZERO);
EXPECT_FP_EQ(neg_zero, LIBC_NAMESPACE::powf(neg_zero, pos_odd_integer));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(neg_zero, pos_even_integer));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(neg_zero, pos_non_integer));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(neg_zero, zero));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(neg_zero, neg_zero));
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::powf(neg_zero, inf));
EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::powf(neg_zero, neg_inf),
FE_DIVBYZERO);
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::powf(neg_zero, aNaN));

// pow( 1.0f, exponent )
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(1.0f, zero));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(1.0f, neg_zero));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(1.0f, 1.0f));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(1.0f, -1.0f));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(1.0f, neg_odd_integer));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(1.0f, neg_even_integer));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(1.0f, neg_non_integer));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(1.0f, pos_odd_integer));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(1.0f, pos_even_integer));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(1.0f, pos_non_integer));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(1.0f, inf));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(1.0f, neg_inf));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(1.0f, aNaN));

// pow( 1.0f, exponent )
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(-1.0f, zero));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(-1.0f, neg_zero));
EXPECT_FP_EQ(-1.0f, LIBC_NAMESPACE::powf(-1.0f, 1.0f));
EXPECT_FP_EQ(-1.0f, LIBC_NAMESPACE::powf(-1.0f, -1.0f));
EXPECT_FP_EQ(-1.0f, LIBC_NAMESPACE::powf(-1.0f, neg_odd_integer));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(-1.0f, neg_even_integer));
EXPECT_FP_IS_NAN_WITH_EXCEPTION(
LIBC_NAMESPACE::powf(-1.0f, neg_non_integer), FE_INVALID);
EXPECT_FP_EQ(-1.0f, LIBC_NAMESPACE::powf(-1.0f, pos_odd_integer));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(-1.0f, pos_even_integer));
EXPECT_FP_IS_NAN_WITH_EXCEPTION(
LIBC_NAMESPACE::powf(-1.0f, pos_non_integer), FE_INVALID);
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(-1.0f, inf));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(-1.0f, neg_inf));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::powf(-1.0f, aNaN));

// pow( inf, exponent )
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(inf, zero));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(inf, neg_zero));
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(inf, 1.0f));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(inf, -1.0f));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(inf, neg_odd_integer));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(inf, neg_even_integer));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(inf, neg_non_integer));
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(inf, pos_odd_integer));
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(inf, pos_even_integer));
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(inf, pos_non_integer));
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(inf, inf));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(inf, neg_inf));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::powf(inf, aNaN));

// pow( -inf, exponent )
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(neg_inf, zero));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(neg_inf, neg_zero));
EXPECT_FP_EQ(neg_inf, LIBC_NAMESPACE::powf(neg_inf, 1.0f));
EXPECT_FP_EQ(neg_zero, LIBC_NAMESPACE::powf(neg_inf, -1.0f));
EXPECT_FP_EQ(neg_zero, LIBC_NAMESPACE::powf(neg_inf, neg_odd_integer));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(neg_inf, neg_even_integer));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(neg_inf, neg_non_integer));
EXPECT_FP_EQ(neg_inf, LIBC_NAMESPACE::powf(neg_inf, pos_odd_integer));
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(neg_inf, pos_even_integer));
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(neg_inf, pos_non_integer));
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(neg_inf, inf));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(neg_inf, neg_inf));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::powf(neg_inf, aNaN));

// pow ( aNaN, exponent )
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(aNaN, zero));
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(aNaN, neg_zero));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::powf(aNaN, 1.0f));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::powf(aNaN, -1.0f));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::powf(aNaN, neg_odd_integer));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::powf(aNaN, neg_even_integer));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::powf(aNaN, neg_non_integer));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::powf(aNaN, pos_odd_integer));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::powf(aNaN, pos_even_integer));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::powf(aNaN, pos_non_integer));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::powf(aNaN, inf));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::powf(aNaN, neg_inf));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::powf(aNaN, aNaN));

// pow ( base, inf )
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(0.1f, inf));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(-0.1f, inf));
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(1.1f, inf));
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(-1.1f, inf));

// pow ( base, -inf )
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(0.1f, neg_inf));
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::powf(-0.1f, neg_inf));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(1.1f, neg_inf));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::powf(-1.1f, neg_inf));

// Exact powers of 2:
EXPECT_FP_EQ(0x1.0p15f, LIBC_NAMESPACE::powf(2.0f, 15.0f));
EXPECT_FP_EQ(0x1.0p126f, LIBC_NAMESPACE::powf(2.0f, 126.0f));
EXPECT_FP_EQ(0x1.0p-45f, LIBC_NAMESPACE::powf(2.0f, -45.0f));
EXPECT_FP_EQ(0x1.0p-126f, LIBC_NAMESPACE::powf(2.0f, -126.0f));
EXPECT_FP_EQ(0x1.0p-149f, LIBC_NAMESPACE::powf(2.0f, -149.0f));

// Exact powers of 10:
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::powf(10.0f, 0.0f));
EXPECT_FP_EQ(10.0f, LIBC_NAMESPACE::powf(10.0f, 1.0f));
EXPECT_FP_EQ(100.0f, LIBC_NAMESPACE::powf(10.0f, 2.0f));
EXPECT_FP_EQ(1000.0f, LIBC_NAMESPACE::powf(10.0f, 3.0f));
EXPECT_FP_EQ(10000.0f, LIBC_NAMESPACE::powf(10.0f, 4.0f));
EXPECT_FP_EQ(100000.0f, LIBC_NAMESPACE::powf(10.0f, 5.0f));
EXPECT_FP_EQ(1000000.0f, LIBC_NAMESPACE::powf(10.0f, 6.0f));
EXPECT_FP_EQ(10000000.0f, LIBC_NAMESPACE::powf(10.0f, 7.0f));
EXPECT_FP_EQ(100000000.0f, LIBC_NAMESPACE::powf(10.0f, 8.0f));
EXPECT_FP_EQ(1000000000.0f, LIBC_NAMESPACE::powf(10.0f, 9.0f));
EXPECT_FP_EQ(10000000000.0f, LIBC_NAMESPACE::powf(10.0f, 10.0f));

// Overflow / Underflow:
if (ROUNDING_MODES[i] != RoundingMode::Downward &&
ROUNDING_MODES[i] != RoundingMode::TowardZero) {
EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::powf(3.1f, 201.0f),
FE_OVERFLOW);
}
if (ROUNDING_MODES[i] != RoundingMode::Upward) {
EXPECT_FP_EQ_WITH_EXCEPTION(0.0f, LIBC_NAMESPACE::powf(3.1f, -201.0f),
FE_UNDERFLOW);
}
}
}
8 changes: 8 additions & 0 deletions libc/utils/MPFRWrapper/MPFRUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,12 @@ class MPFRNumber {
return result;
}

MPFRNumber pow(const MPFRNumber &b) {
MPFRNumber result(*this);
mpfr_pow(result.value, value, b.value, mpfr_rounding);
return result;
}

MPFRNumber remquo(const MPFRNumber &divisor, int &quotient) {
MPFRNumber remainder(*this);
long q;
Expand Down Expand Up @@ -626,6 +632,8 @@ binary_operation_one_output(Operation op, InputType x, InputType y,
return inputX.fmod(inputY);
case Operation::Hypot:
return inputX.hypot(inputY);
case Operation::Pow:
return inputX.pow(inputY);
default:
__builtin_unreachable();
}
Expand Down
1 change: 1 addition & 0 deletions libc/utils/MPFRWrapper/MPFRUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ enum class Operation : int {
BeginBinaryOperationsSingleOutput,
Fmod,
Hypot,
Pow,
EndBinaryOperationsSingleOutput,

// Operations which take two floating point numbers of the same type as
Expand Down