18 changes: 18 additions & 0 deletions libc/src/math/log2f.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//===-- Implementation header for log2f -------------------------*- C++ -*-===//
//
// 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_LOG2F_H
#define LLVM_LIBC_SRC_MATH_LOG2F_H

namespace __llvm_libc {

float log2f(float x);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_MATH_LOG2F_H
13 changes: 13 additions & 0 deletions libc/test/src/math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,19 @@ add_fp_unittest(
libc.src.__support.FPUtil.fputil
)

add_fp_unittest(
log2f_test
NEED_MPFR
SUITE
libc_math_unittests
SRCS
log2f_test.cpp
DEPENDS
libc.include.math
libc.src.math.log2f
libc.src.__support.FPUtil.fputil
)

add_subdirectory(generic)
add_subdirectory(exhaustive)
add_subdirectory(differential_testing)
22 changes: 22 additions & 0 deletions libc/test/src/math/differential_testing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,28 @@ add_diff_binary(
-fno-builtin
)

add_diff_binary(
log2f_diff
SRCS
log2f_diff.cpp
DEPENDS
.single_input_single_output_diff
libc.src.math.log2f
COMPILE_OPTIONS
-fno-builtin
)

add_diff_binary(
log2f_perf
SRCS
log2f_perf.cpp
DEPENDS
.single_input_single_output_diff
libc.src.math.log2f
COMPILE_OPTIONS
-fno-builtin
)

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

#include <math.h>

SINGLE_INPUT_SINGLE_OUTPUT_DIFF(float, __llvm_libc::log2f, ::log2f,
"log2f_diff.log")
16 changes: 16 additions & 0 deletions libc/test/src/math/differential_testing/log2f_perf.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//===-- Differential test for log2f ---------------------------------------===//
//
// 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/log2f.h"

#include <math.h>

SINGLE_INPUT_SINGLE_OUTPUT_PERF(float, __llvm_libc::log2f, ::log2f,
"log2f_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 @@ -78,3 +78,20 @@ add_fp_unittest(
LINK_OPTIONS
-lpthread
)

add_fp_unittest(
log2f_test
NO_RUN_POSTBUILD
NEED_MPFR
SUITE
libc_math_exhaustive_tests
SRCS
log2f_test.cpp
DEPENDS
.exhaustive_test
libc.include.math
libc.src.math.log2f
libc.src.__support.FPUtil.fputil
LINK_OPTIONS
-lpthread
)
51 changes: 51 additions & 0 deletions libc/test/src/math/exhaustive/log2f_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//===-- Exhaustive test for log2f -----------------------------------------===//
//
// 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/log2f.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 LlvmLibcLog2fExhaustiveTest : 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::Log2, x, __llvm_libc::log2f(x), 0.5,
rounding);
} while (bits++ < stop);
}
};

TEST_F(LlvmLibcLog2fExhaustiveTest, RoundNearestTieToEven) {
test_full_range(/*start=*/0U, /*stop=*/0x7f80'0000U, /*nthreads=*/16,
mpfr::RoundingMode::Nearest);
}

TEST_F(LlvmLibcLog2fExhaustiveTest, RoundUp) {
test_full_range(/*start=*/0U, /*stop=*/0x7f80'0000U, /*nthreads=*/16,
mpfr::RoundingMode::Upward);
}

TEST_F(LlvmLibcLog2fExhaustiveTest, RoundDown) {
test_full_range(/*start=*/0U, /*stop=*/0x7f80'0000U, /*nthreads=*/16,
mpfr::RoundingMode::Downward);
}

TEST_F(LlvmLibcLog2fExhaustiveTest, RoundTowardZero) {
test_full_range(/*start=*/0U, /*stop=*/0x7f80'0000U, /*nthreads=*/16,
mpfr::RoundingMode::TowardZero);
}
64 changes: 64 additions & 0 deletions libc/test/src/math/log2f_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//===-- Unittests for log2f -----------------------------------------------===//
//
// 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/log2f.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(LlvmLibcLog2fTest, SpecialNumbers) {
EXPECT_FP_EQ(aNaN, __llvm_libc::log2f(aNaN));
EXPECT_FP_EQ(inf, __llvm_libc::log2f(inf));
EXPECT_TRUE(FPBits(__llvm_libc::log2f(neg_inf)).is_nan());
EXPECT_FP_EQ(neg_inf, __llvm_libc::log2f(0.0f));
EXPECT_FP_EQ(neg_inf, __llvm_libc::log2f(-0.0f));
EXPECT_TRUE(FPBits(__llvm_libc::log2f(-1.0f)).is_nan());
EXPECT_FP_EQ(zero, __llvm_libc::log2f(1.0f));
}

TEST(LlvmLibcLog2fTest, TrickyInputs) {
constexpr int N = 9;
constexpr uint32_t INPUTS[N] = {0x3f7d57f5U, 0x3f7ed848U, 0x3f7fd6ccU,
0x3f7fffffU, 0x3f80079bU, 0x3f81d0b5U,
0x3f82e602U, 0x3f83c98dU, 0x3f8cba39U};

for (int i = 0; i < N; ++i) {
float x = float(FPBits(INPUTS[i]));
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Log2, x,
__llvm_libc::log2f(x), 0.5);
}
}

TEST(LlvmLibcLog2fTest, 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::log2f(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::Log2, x,
__llvm_libc::log2f(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 @@ -267,6 +267,12 @@ class MPFRNumber {
return result;
}

MPFRNumber log2() const {
MPFRNumber result(*this);
mpfr_log2(result.value, value, mpfr_rounding);
return result;
}

MPFRNumber remquo(const MPFRNumber &divisor, int &quotient) {
MPFRNumber remainder(*this);
long q;
Expand Down Expand Up @@ -494,6 +500,8 @@ unary_operation(Operation op, InputType input, unsigned int precision,
return mpfrInput.floor();
case Operation::Log:
return mpfrInput.log();
case Operation::Log2:
return mpfrInput.log2();
case Operation::Mod2PI:
return mpfrInput.mod_2pi();
case Operation::ModPIOver2:
Expand Down
35 changes: 35 additions & 0 deletions libc/utils/MPFRWrapper/MPFRUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ enum class Operation : int {
Expm1,
Floor,
Log,
Log2,
Mod2PI,
ModPIOver2,
ModPIOver4,
Expand Down Expand Up @@ -325,6 +326,23 @@ template <typename T> bool round_to_long(T x, RoundingMode mode, long &result);
EXPECT_MPFR_MATCH_DEFAULT, GET_MPFR_DUMMY_ARG) \
(__VA_ARGS__)

#define EXPECT_MPFR_MATCH_ALL_ROUNDING(op, input, match_value, ulp_tolerance) \
{ \
namespace mpfr = __llvm_libc::testing::mpfr; \
mpfr::ForceRoundingMode __r1(mpfr::RoundingMode::Nearest); \
EXPECT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
mpfr::RoundingMode::Nearest); \
mpfr::ForceRoundingMode __r2(mpfr::RoundingMode::Upward); \
EXPECT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
mpfr::RoundingMode::Upward); \
mpfr::ForceRoundingMode __r3(mpfr::RoundingMode::Downward); \
EXPECT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
mpfr::RoundingMode::Downward); \
mpfr::ForceRoundingMode __r4(mpfr::RoundingMode::TowardZero); \
EXPECT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
mpfr::RoundingMode::TowardZero); \
}

#define ASSERT_MPFR_MATCH_DEFAULT(op, input, match_value, ulp_tolerance) \
ASSERT_THAT(match_value, \
__llvm_libc::testing::mpfr::get_mpfr_matcher<op>( \
Expand All @@ -341,4 +359,21 @@ template <typename T> bool round_to_long(T x, RoundingMode mode, long &result);
ASSERT_MPFR_MATCH_DEFAULT, GET_MPFR_DUMMY_ARG) \
(__VA_ARGS__)

#define ASSERT_MPFR_MATCH_ALL_ROUNDING(op, input, match_value, ulp_tolerance) \
{ \
namespace mpfr = __llvm_libc::testing::mpfr; \
mpfr::ForceRoundingMode __r1(mpfr::RoundingMode::Nearest); \
ASSERT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
mpfr::RoundingMode::Nearest); \
mpfr::ForceRoundingMode __r2(mpfr::RoundingMode::Upward); \
ASSERT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
mpfr::RoundingMode::Upward); \
mpfr::ForceRoundingMode __r3(mpfr::RoundingMode::Downward); \
ASSERT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
mpfr::RoundingMode::Downward); \
mpfr::ForceRoundingMode __r4(mpfr::RoundingMode::TowardZero); \
ASSERT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
mpfr::RoundingMode::TowardZero); \
}

#endif // LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H