11 changes: 11 additions & 0 deletions libc/test/src/math/differential_testing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,17 @@ add_diff_binary(
-fno-builtin
)

add_diff_binary(
log1pf_perf
SRCS
log1pf_perf.cpp
DEPENDS
.single_input_single_output_diff
libc.src.math.log1pf
COMPILE_OPTIONS
-fno-builtin
)

add_diff_binary(
log2f_diff
SRCS
Expand Down
16 changes: 16 additions & 0 deletions libc/test/src/math/differential_testing/log1pf_perf.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//===-- Differential test for log1pf --------------------------------------===//
//
// 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 "SingleInputSingleOutputDiff.h"

#include "src/math/log1pf.h"

#include <math.h>

SINGLE_INPUT_SINGLE_OUTPUT_PERF(float, __llvm_libc::log1pf, ::log1pf,
"log1pf_perf.log")
17 changes: 17 additions & 0 deletions libc/test/src/math/exhaustive/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,23 @@ add_fp_unittest(
-lpthread
)

add_fp_unittest(
log1pf_test
NO_RUN_POSTBUILD
NEED_MPFR
SUITE
libc_math_exhaustive_tests
SRCS
log1pf_test.cpp
DEPENDS
.exhaustive_test
libc.include.math
libc.src.math.log1pf
libc.src.__support.FPUtil.fputil
LINK_OPTIONS
-lpthread
)

add_fp_unittest(
log2f_test
NO_RUN_POSTBUILD
Expand Down
55 changes: 55 additions & 0 deletions libc/test/src/math/exhaustive/log1pf_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//===-- Exhaustive test for log1pf ----------------------------------------===//
//
// 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/log1pf.h"
#include "utils/MPFRWrapper/MPFRUtils.h"
#include "utils/UnitTest/FPMatcher.h"

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

namespace mpfr = __llvm_libc::testing::mpfr;

struct LlvmLibclog1pfExhaustiveTest : public LlvmLibcExhaustiveTest<uint32_t> {
void check(uint32_t start, uint32_t stop,
mpfr::RoundingMode rounding) override {
mpfr::ForceRoundingMode r(rounding);
uint32_t bits = start;
do {
FPBits xbits(bits);
float x = float(xbits);
EXPECT_MPFR_MATCH(mpfr::Operation::Log1p, x, __llvm_libc::log1pf(x), 0.5,
rounding);
} while (bits++ < stop);
}
};

// Range: All non-negative;
static constexpr uint32_t START = 0x0000'0000U;
static constexpr uint32_t STOP = 0x7f80'0000U;
// Range: [-1, 0];
// static constexpr uint32_t START = 0x8000'0000U;
// static constexpr uint32_t STOP = 0xbf80'0000U;
static constexpr int NUM_THREADS = 16;

TEST_F(LlvmLibclog1pfExhaustiveTest, RoundNearestTieToEven) {
test_full_range(START, STOP, NUM_THREADS, mpfr::RoundingMode::Nearest);
}

TEST_F(LlvmLibclog1pfExhaustiveTest, RoundUp) {
test_full_range(START, STOP, NUM_THREADS, mpfr::RoundingMode::Upward);
}

TEST_F(LlvmLibclog1pfExhaustiveTest, RoundDown) {
test_full_range(START, STOP, NUM_THREADS, mpfr::RoundingMode::Downward);
}

TEST_F(LlvmLibclog1pfExhaustiveTest, RoundTowardZero) {
test_full_range(START, STOP, NUM_THREADS, mpfr::RoundingMode::TowardZero);
}
81 changes: 81 additions & 0 deletions libc/test/src/math/log1pf_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//===-- Unittests for log1pf ----------------------------------------------===//
//
// 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/log1pf.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(LlvmLibclog1pfTest, SpecialNumbers) {
EXPECT_FP_EQ(aNaN, __llvm_libc::log1pf(aNaN));
EXPECT_FP_EQ(inf, __llvm_libc::log1pf(inf));
EXPECT_TRUE(FPBits((__llvm_libc::log1pf(neg_inf))).is_nan());
EXPECT_FP_EQ(zero, __llvm_libc::log1pf(0.0f));
EXPECT_FP_EQ(neg_zero, __llvm_libc::log1pf(-0.0f));
EXPECT_FP_EQ(neg_inf, __llvm_libc::log1pf(-1.0f));
}

TEST(LlvmLibclog1pfTest, TrickyInputs) {
constexpr int N = 20;
constexpr uint32_t INPUTS[N] = {
0x35c00006U, /*0x1.80000cp-20f*/
0x35400003U, /*0x1.800006p-21f*/
0x3640000cU, /*0x1.800018p-19f*/
0x36c00018U, /*0x1.80003p-18f*/
0x3710001bU, /*0x1.200036p-17f*/
0x37400030U, /*0x1.80006p-17f*/
0x3770004bU, /*0x1.e00096p-17f*/
0x3b9315c8U, /*0x1.262b9p-8f*/
0x3c6eb7afU, /*0x1.dd6f5ep-7f*/
0x41078febU, /*0x1.0f1fd6p+3f*/
0x5cd69e88U, /*0x1.ad3d1p+58f*/
0x65d890d3U, /*0x1.b121a6p+76f*/
0x6f31a8ecU, /*0x1.6351d8p+95f*/
0x7a17f30aU, /*0x1.2fe614p+117f*/
0xb53ffffdU, /*-0x1.7ffffap-21f*/
0xb70fffe5U, /*-0x1.1fffcap-17f*/
0xbb0ec8c4U, /*-0x1.1d9188p-9f*/
0xbc4d092cU, /*-0x1.9a1258p-7f*/
0xbc657728U, /*-0x1.caee5p-7f*/
0xbd1d20afU, /*-0x1.3a415ep-5f*/
};
for (int i = 0; i < N; ++i) {
float x = float(FPBits(INPUTS[i]));
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Log1p, x,
__llvm_libc::log1pf(x), 0.5);
}
}

TEST(LlvmLibclog1pfTest, 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::log1pf(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::Log1p, x,
__llvm_libc::log1pf(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 @@ -279,6 +279,12 @@ class MPFRNumber {
return result;
}

MPFRNumber log1p() const {
MPFRNumber result(*this);
mpfr_log1p(result.value, value, mpfr_rounding);
return result;
}

MPFRNumber remquo(const MPFRNumber &divisor, int &quotient) {
MPFRNumber remainder(*this);
long q;
Expand Down Expand Up @@ -510,6 +516,8 @@ unary_operation(Operation op, InputType input, unsigned int precision,
return mpfrInput.log2();
case Operation::Log10:
return mpfrInput.log10();
case Operation::Log1p:
return mpfrInput.log1p();
case Operation::Mod2PI:
return mpfrInput.mod_2pi();
case Operation::ModPIOver2:
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 @@ -33,6 +33,7 @@ enum class Operation : int {
Log,
Log2,
Log10,
Log1p,
Mod2PI,
ModPIOver2,
ModPIOver4,
Expand Down