498 changes: 498 additions & 0 deletions libc/src/math/generic/pow.cpp

Large diffs are not rendered by default.

12 changes: 0 additions & 12 deletions libc/src/math/nvptx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -397,18 +397,6 @@ add_entrypoint_object(
VENDOR
)

add_entrypoint_object(
pow
SRCS
pow.cpp
HDRS
../pow.h
COMPILE_OPTIONS
${bitcode_link_flags}
-O2
VENDOR
)

add_entrypoint_object(
powi
SRCS
Expand Down
19 changes: 0 additions & 19 deletions libc/src/math/nvptx/pow.cpp

This file was deleted.

11 changes: 11 additions & 0 deletions libc/test/src/math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2045,6 +2045,17 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
pow_test
NEED_MPFR
SUITE
libc-math-unittests
SRCS
pow_test.cpp
DEPENDS
libc.src.math.pow
)

add_fp_unittest(
powf_test
NEED_MPFR
Expand Down
112 changes: 112 additions & 0 deletions libc/test/src/math/pow_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//===-- Unittests for pow -------------------------------------------------===//
//
// 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/math/pow.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
#include "utils/MPFRWrapper/MPFRUtils.h"

using LlvmLibcPowTest = LIBC_NAMESPACE::testing::FPTest<double>;
using LIBC_NAMESPACE::testing::tlog;

namespace mpfr = LIBC_NAMESPACE::testing::mpfr;

TEST_F(LlvmLibcPowTest, TrickyInputs) {
constexpr mpfr::BinaryInput<double> INPUTS[] = {
{0x1.0853408534085p-2, 0x1.0D148E03BCBA8p-1},
{0x1.65FBD65FBD657p-1, 0x1.F10D148E03BB6p+1},
};

for (auto input : INPUTS) {
double x = input.x;
double y = input.y;
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Pow, input,
LIBC_NAMESPACE::pow(x, y), 0.5);
}
}

TEST_F(LlvmLibcPowTest, InFloatRange) {
constexpr uint64_t X_COUNT = 123;
constexpr uint64_t X_START = FPBits(0.25).uintval();
constexpr uint64_t X_STOP = FPBits(4.0).uintval();
constexpr uint64_t X_STEP = (X_STOP - X_START) / X_COUNT;

constexpr uint64_t Y_COUNT = 137;
constexpr uint64_t Y_START = FPBits(0.25).uintval();
constexpr uint64_t Y_STOP = FPBits(4.0).uintval();
constexpr uint64_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;
double mx = 0.0, my = 0.0, mr = 0.0;
double tol = 1.5;

for (uint64_t i = 0, v = X_START; i <= X_COUNT; ++i, v += X_STEP) {
double x = FPBits(v).get_val();
if (FPBits(x).is_inf_or_nan() || x < 0.0)
continue;

for (uint64_t j = 0, w = Y_START; j <= Y_COUNT; ++j, w += Y_STEP) {
double y = FPBits(w).get_val();
if (FPBits(y).is_inf_or_nan())
continue;

double result = LIBC_NAMESPACE::pow(x, y);
++cc;
if (FPBits(result).is_inf_or_nan())
continue;

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

if (!TEST_MPFR_MATCH_ROUNDING_SILENTLY(mpfr::Operation::Pow, inputs,
result, 1.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 << " Pow failed: " << fails << "/" << count << "/" << cc
<< " tests.\n"
<< " Max ULPs is at most: " << static_cast<uint64_t>(tol) << ".\n";
}
if (fails) {
mpfr::BinaryInput<double> inputs{mx, my};
EXPECT_MPFR_MATCH(mpfr::Operation::Pow, inputs, mr, 1.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);
}
11 changes: 11 additions & 0 deletions libc/test/src/math/smoke/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3695,6 +3695,17 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
pow_test
SUITE
libc-math-smoke-tests
SRCS
pow_test.cpp
DEPENDS
libc.hdr.fenv_macros
libc.src.math.pow
)

add_fp_unittest(
powf_test
SUITE
Expand Down
187 changes: 187 additions & 0 deletions libc/test/src/math/smoke/pow_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
//===-- Unittests for pow -------------------------------------------------===//
//
// 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 "hdr/fenv_macros.h"
#include "src/math/pow.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"

using LlvmLibcPowTest = LIBC_NAMESPACE::testing::FPTest<double>;
using LIBC_NAMESPACE::fputil::testing::ForceRoundingMode;
using LIBC_NAMESPACE::fputil::testing::RoundingMode;

TEST_F(LlvmLibcPowTest, SpecialNumbers) {
constexpr double NEG_ODD_INTEGER = -3.0;
constexpr double NEG_EVEN_INTEGER = -6.0;
constexpr double NEG_NON_INTEGER = -1.1;
constexpr double POS_ODD_INTEGER = 5.0;
constexpr double POS_EVEN_INTEGER = 8.0;
constexpr double POS_NON_INTEGER = 1.1;

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

// pow( 0.0, exponent )
EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::pow(zero, NEG_ODD_INTEGER),
FE_DIVBYZERO);
EXPECT_FP_EQ_WITH_EXCEPTION(
inf, LIBC_NAMESPACE::pow(zero, NEG_EVEN_INTEGER), FE_DIVBYZERO);
EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::pow(zero, NEG_NON_INTEGER),
FE_DIVBYZERO);
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(zero, POS_ODD_INTEGER));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(zero, POS_EVEN_INTEGER));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(zero, POS_NON_INTEGER));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(zero, zero));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(zero, neg_zero));
EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::pow(zero, inf));
EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::pow(zero, neg_inf),
FE_DIVBYZERO);
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::pow(zero, aNaN));

// pow( -0.0, exponent )
EXPECT_FP_EQ_WITH_EXCEPTION(
neg_inf, LIBC_NAMESPACE::pow(neg_zero, NEG_ODD_INTEGER), FE_DIVBYZERO);
EXPECT_FP_EQ_WITH_EXCEPTION(
inf, LIBC_NAMESPACE::pow(neg_zero, NEG_EVEN_INTEGER), FE_DIVBYZERO);
EXPECT_FP_EQ_WITH_EXCEPTION(
inf, LIBC_NAMESPACE::pow(neg_zero, NEG_NON_INTEGER), FE_DIVBYZERO);
EXPECT_FP_EQ(neg_zero, LIBC_NAMESPACE::pow(neg_zero, POS_ODD_INTEGER));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(neg_zero, POS_EVEN_INTEGER));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(neg_zero, POS_NON_INTEGER));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(neg_zero, zero));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(neg_zero, neg_zero));
EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::pow(neg_zero, inf));
EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::pow(neg_zero, neg_inf),
FE_DIVBYZERO);
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::pow(neg_zero, aNaN));

// pow( 1.0, exponent )
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(1.0, zero));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(1.0, neg_zero));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(1.0, 1.0));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(1.0, -1.0));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(1.0, NEG_ODD_INTEGER));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(1.0, NEG_EVEN_INTEGER));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(1.0, NEG_NON_INTEGER));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(1.0, POS_ODD_INTEGER));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(1.0, POS_EVEN_INTEGER));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(1.0, POS_NON_INTEGER));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(1.0, inf));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(1.0, neg_inf));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(1.0, aNaN));

// pow( 1.0, exponent )
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(-1.0, zero));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(-1.0, neg_zero));
EXPECT_FP_EQ(-1.0, LIBC_NAMESPACE::pow(-1.0, 1.0));
EXPECT_FP_EQ(-1.0, LIBC_NAMESPACE::pow(-1.0, -1.0));
EXPECT_FP_EQ(-1.0, LIBC_NAMESPACE::pow(-1.0, NEG_ODD_INTEGER));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(-1.0, NEG_EVEN_INTEGER));
EXPECT_FP_IS_NAN_WITH_EXCEPTION(LIBC_NAMESPACE::pow(-1.0, NEG_NON_INTEGER),
FE_INVALID);
EXPECT_FP_EQ(-1.0, LIBC_NAMESPACE::pow(-1.0, POS_ODD_INTEGER));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(-1.0, POS_EVEN_INTEGER));
EXPECT_FP_IS_NAN_WITH_EXCEPTION(LIBC_NAMESPACE::pow(-1.0, POS_NON_INTEGER),
FE_INVALID);
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(-1.0, inf));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(-1.0, neg_inf));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::pow(-1.0, aNaN));

// pow( inf, exponent )
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(inf, zero));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(inf, neg_zero));
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::pow(inf, 1.0));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(inf, -1.0));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(inf, NEG_ODD_INTEGER));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(inf, NEG_EVEN_INTEGER));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(inf, NEG_NON_INTEGER));
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::pow(inf, POS_ODD_INTEGER));
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::pow(inf, POS_EVEN_INTEGER));
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::pow(inf, POS_NON_INTEGER));
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::pow(inf, inf));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(inf, neg_inf));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::pow(inf, aNaN));

// pow( -inf, exponent )
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(neg_inf, zero));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(neg_inf, neg_zero));
EXPECT_FP_EQ(neg_inf, LIBC_NAMESPACE::pow(neg_inf, 1.0));
EXPECT_FP_EQ(neg_zero, LIBC_NAMESPACE::pow(neg_inf, -1.0));
EXPECT_FP_EQ(neg_zero, LIBC_NAMESPACE::pow(neg_inf, NEG_ODD_INTEGER));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(neg_inf, NEG_EVEN_INTEGER));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(neg_inf, NEG_NON_INTEGER));
EXPECT_FP_EQ(neg_inf, LIBC_NAMESPACE::pow(neg_inf, POS_ODD_INTEGER));
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::pow(neg_inf, POS_EVEN_INTEGER));
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::pow(neg_inf, POS_NON_INTEGER));
EXPECT_FP_EQ(inf, LIBC_NAMESPACE::pow(neg_inf, inf));
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::pow(neg_inf, neg_inf));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::pow(neg_inf, aNaN));

// pow ( aNaN, exponent )
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(aNaN, zero));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(aNaN, neg_zero));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::pow(aNaN, 1.0));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::pow(aNaN, -1.0));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::pow(aNaN, NEG_ODD_INTEGER));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::pow(aNaN, NEG_EVEN_INTEGER));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::pow(aNaN, NEG_NON_INTEGER));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::pow(aNaN, POS_ODD_INTEGER));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::pow(aNaN, POS_EVEN_INTEGER));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::pow(aNaN, POS_NON_INTEGER));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::pow(aNaN, inf));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::pow(aNaN, neg_inf));
EXPECT_FP_IS_NAN(LIBC_NAMESPACE::pow(aNaN, aNaN));

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

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

// Exact powers of 2:
// TODO: Enable these tests when we use exp2.
// EXPECT_FP_EQ(0x1.0p15, LIBC_NAMESPACE::pow(2.0, 15.0));
// EXPECT_FP_EQ(0x1.0p126, LIBC_NAMESPACE::pow(2.0, 126.0));
// EXPECT_FP_EQ(0x1.0p-45, LIBC_NAMESPACE::pow(2.0, -45.0));
// EXPECT_FP_EQ(0x1.0p-126, LIBC_NAMESPACE::pow(2.0, -126.0));
// EXPECT_FP_EQ(0x1.0p-149, LIBC_NAMESPACE::pow(2.0, -149.0));

// Exact powers of 10:
// TODO: Enable these tests when we use exp10
// EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::pow(10.0, 0.0));
// EXPECT_FP_EQ(10.0, LIBC_NAMESPACE::pow(10.0, 1.0));
// EXPECT_FP_EQ(100.0, LIBC_NAMESPACE::pow(10.0, 2.0));
// EXPECT_FP_EQ(1000.0, LIBC_NAMESPACE::pow(10.0, 3.0));
// EXPECT_FP_EQ(10000.0, LIBC_NAMESPACE::pow(10.0, 4.0));
// EXPECT_FP_EQ(100000.0, LIBC_NAMESPACE::pow(10.0, 5.0));
// EXPECT_FP_EQ(1000000.0, LIBC_NAMESPACE::pow(10.0, 6.0));
// EXPECT_FP_EQ(10000000.0, LIBC_NAMESPACE::pow(10.0, 7.0));
// EXPECT_FP_EQ(100000000.0, LIBC_NAMESPACE::pow(10.0, 8.0));
// EXPECT_FP_EQ(1000000000.0, LIBC_NAMESPACE::pow(10.0, 9.0));
// EXPECT_FP_EQ(10000000000.0, LIBC_NAMESPACE::pow(10.0, 10.0));

// Overflow / Underflow:
if (ROUNDING_MODES[i] != RoundingMode::Downward &&
ROUNDING_MODES[i] != RoundingMode::TowardZero) {
EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::pow(3.1, 2001.0),
FE_OVERFLOW);
}
if (ROUNDING_MODES[i] != RoundingMode::Upward) {
EXPECT_FP_EQ_WITH_EXCEPTION(0.0, LIBC_NAMESPACE::pow(3.1, -2001.0),
FE_UNDERFLOW);
}
}
}