| 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 |
| 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 |
| 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); | ||
| } |
| 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); | ||
| } | ||
| } | ||
| } |