75 changes: 75 additions & 0 deletions libc/test/src/math/exhaustive/exp10f_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//===-- Exhaustive test for exp10f ----------------------------------------===//
//
// 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 "exhaustive_test.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/math/exp10f.h"
#include "utils/MPFRWrapper/MPFRUtils.h"
#include "utils/UnitTest/FPMatcher.h"

#include <thread>

using FPBits = __llvm_libc::fputil::FPBits<float>;

namespace mpfr = __llvm_libc::testing::mpfr;

struct LlvmLibcExp10fExhaustiveTest : public LlvmLibcExhaustiveTest<uint32_t> {
bool check(uint32_t start, uint32_t stop,
mpfr::RoundingMode rounding) override {
mpfr::ForceRoundingMode r(rounding);
uint32_t bits = start;
bool result = true;
do {
FPBits xbits(bits);
float x = float(xbits);
result &= EXPECT_MPFR_MATCH(mpfr::Operation::Exp10, x,
__llvm_libc::exp10f(x), 0.5, rounding);
} while (bits++ < stop);
return result;
}
};

// Range: [0, 89];
static constexpr uint32_t POS_START = 0x0000'0000U;
static constexpr uint32_t POS_STOP = 0x42b2'0000U;

TEST_F(LlvmLibcExp10fExhaustiveTest, PostiveRangeRoundNearestTieToEven) {
test_full_range(POS_START, POS_STOP, mpfr::RoundingMode::Nearest);
}

TEST_F(LlvmLibcExp10fExhaustiveTest, PostiveRangeRoundUp) {
test_full_range(POS_START, POS_STOP, mpfr::RoundingMode::Upward);
}

TEST_F(LlvmLibcExp10fExhaustiveTest, PostiveRangeRoundDown) {
test_full_range(POS_START, POS_STOP, mpfr::RoundingMode::Downward);
}

TEST_F(LlvmLibcExp10fExhaustiveTest, PostiveRangeRoundTowardZero) {
test_full_range(POS_START, POS_STOP, mpfr::RoundingMode::TowardZero);
}

// Range: [-104, 0];
static constexpr uint32_t NEG_START = 0x8000'0000U;
static constexpr uint32_t NEG_STOP = 0xc2d0'0000U;

TEST_F(LlvmLibcExp10fExhaustiveTest, NegativeRangeRoundNearestTieToEven) {
test_full_range(NEG_START, NEG_STOP, mpfr::RoundingMode::Nearest);
}

TEST_F(LlvmLibcExp10fExhaustiveTest, NegativeRangeRoundUp) {
test_full_range(NEG_START, NEG_STOP, mpfr::RoundingMode::Upward);
}

TEST_F(LlvmLibcExp10fExhaustiveTest, NegativeRangeRoundDown) {
test_full_range(NEG_START, NEG_STOP, mpfr::RoundingMode::Downward);
}

TEST_F(LlvmLibcExp10fExhaustiveTest, NegativeRangeRoundTowardZero) {
test_full_range(NEG_START, NEG_STOP, mpfr::RoundingMode::TowardZero);
}
107 changes: 107 additions & 0 deletions libc/test/src/math/exp10f_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//===-- Unittests for exp10f ----------------------------------------------===//
//
// 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/exp10f.h"
#include "utils/MPFRWrapper/MPFRUtils.h"
#include "utils/UnitTest/FPMatcher.h"
#include "utils/UnitTest/Test.h"
#include <math.h>

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

namespace mpfr = __llvm_libc::testing::mpfr;

DECLARE_SPECIAL_CONSTANTS(float)

TEST(LlvmLibcExp10fTest, SpecialNumbers) {
errno = 0;

EXPECT_FP_EQ(aNaN, __llvm_libc::exp10f(aNaN));
EXPECT_MATH_ERRNO(0);

EXPECT_FP_EQ(inf, __llvm_libc::exp10f(inf));
EXPECT_MATH_ERRNO(0);

EXPECT_FP_EQ(0.0f, __llvm_libc::exp10f(neg_inf));
EXPECT_MATH_ERRNO(0);

EXPECT_FP_EQ(1.0f, __llvm_libc::exp10f(0.0f));
EXPECT_MATH_ERRNO(0);

EXPECT_FP_EQ(1.0f, __llvm_libc::exp10f(-0.0f));
EXPECT_MATH_ERRNO(0);
}

TEST(LlvmLibcExp10fTest, Overflow) {
errno = 0;
EXPECT_FP_EQ(inf, __llvm_libc::exp10f(float(FPBits(0x7f7fffffU))));
EXPECT_MATH_ERRNO(ERANGE);

EXPECT_FP_EQ(inf, __llvm_libc::exp10f(float(FPBits(0x43000000U))));
EXPECT_MATH_ERRNO(ERANGE);

EXPECT_FP_EQ(inf, __llvm_libc::exp10f(float(FPBits(0x43000001U))));
EXPECT_MATH_ERRNO(ERANGE);
}

TEST(LlvmLibcExp10fTest, TrickyInputs) {
constexpr int N = 20;
constexpr uint32_t INPUTS[N] = {
0x325e5bd8, // x = 0x1.bcb7bp-27f
0x325e5bd9, // x = 0x1.bcb7b2p-27f
0x325e5bda, // x = 0x1.bcb7b4p-27f
0x3d14d956, // x = 0x1.29b2acp-5f
0x4116498a, // x = 0x1.2c9314p3f
0x4126f431, // x = 0x1.4de862p3f
0x4187d13c, // x = 0x1.0fa278p4f
0x4203e9da, // x = 0x1.07d3b4p5f
0x420b5f5d, // x = 0x1.16bebap5f
0x42349e35, // x = 0x1.693c6ap5f
0x3f800000, // x = 1.0f
0x40000000, // x = 2.0f
0x40400000, // x = 3.0f
0x40800000, // x = 4.0f
0x40a00000, // x = 5.0f
0x40c00000, // x = 6.0f
0x40e00000, // x = 7.0f
0x41000000, // x = 8.0f
0x41100000, // x = 9.0f
0x41200000, // x = 10.0f
};
for (int i = 0; i < N; ++i) {
errno = 0;
float x = float(FPBits(INPUTS[i]));
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp10, x,
__llvm_libc::exp10f(x), 0.5);
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp10, -x,
__llvm_libc::exp10f(-x), 0.5);
}
}

TEST(LlvmLibcExp10fTest, InFloatRange) {
constexpr uint32_t COUNT = 1000000;
constexpr uint32_t STEP = UINT32_MAX / COUNT;
for (uint32_t i = 0, v = 0; i <= COUNT; ++i, v += STEP) {
float x = float(FPBits(v));
if (isnan(x) || isinf(x))
continue;
errno = 0;
float result = __llvm_libc::exp10f(x);

// If the computation resulted in an error or did not produce valid result
// in the single-precision floating point range, then ignore comparing with
// MPFR result as MPFR can still produce valid results because of its
// wider precision.
if (isnan(result) || isinf(result) || errno != 0)
continue;
ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp10, x,
__llvm_libc::exp10f(x), 0.5);
}
}
8 changes: 8 additions & 0 deletions libc/utils/MPFRWrapper/MPFRUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,12 @@ class MPFRNumber {
return result;
}

MPFRNumber exp10() const {
MPFRNumber result(*this);
mpfr_exp10(result.value, value, mpfr_rounding);
return result;
}

MPFRNumber expm1() const {
MPFRNumber result(*this);
mpfr_expm1(result.value, value, mpfr_rounding);
Expand Down Expand Up @@ -550,6 +556,8 @@ unary_operation(Operation op, InputType input, unsigned int precision,
return mpfrInput.exp();
case Operation::Exp2:
return mpfrInput.exp2();
case Operation::Exp10:
return mpfrInput.exp10();
case Operation::Expm1:
return mpfrInput.expm1();
case Operation::Floor:
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 @@ -34,6 +34,7 @@ enum class Operation : int {
Cosh,
Exp,
Exp2,
Exp10,
Expm1,
Floor,
Log,
Expand Down