40 changes: 40 additions & 0 deletions libc/test/src/math/exp10m1f16_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//===-- Exhaustive test for exp10m1f16 ------------------------------------===//
//
// 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/exp10m1f16.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
#include "utils/MPFRWrapper/MPFRUtils.h"

using LlvmLibcExp10m1f16Test = LIBC_NAMESPACE::testing::FPTest<float16>;

namespace mpfr = LIBC_NAMESPACE::testing::mpfr;

// Range: [0, Inf];
static constexpr uint16_t POS_START = 0x0000U;
static constexpr uint16_t POS_STOP = 0x7c00U;

// Range: [-Inf, 0];
static constexpr uint16_t NEG_START = 0x8000U;
static constexpr uint16_t NEG_STOP = 0xfc00U;

TEST_F(LlvmLibcExp10m1f16Test, PositiveRange) {
for (uint16_t v = POS_START; v <= POS_STOP; ++v) {
float16 x = FPBits(v).get_val();
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp10m1, x,
LIBC_NAMESPACE::exp10m1f16(x), 0.5);
}
}

TEST_F(LlvmLibcExp10m1f16Test, NegativeRange) {
for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) {
float16 x = FPBits(v).get_val();
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp10m1, x,
LIBC_NAMESPACE::exp10m1f16(x), 0.5);
}
}
13 changes: 13 additions & 0 deletions libc/test/src/math/smoke/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1235,6 +1235,19 @@ add_fp_unittest(
libc.src.__support.FPUtil.cast
)

add_fp_unittest(
exp10m1f16_test
SUITE
libc-math-smoke-tests
SRCS
exp10m1f16_test.cpp
DEPENDS
libc.hdr.fenv_macros
libc.src.errno.errno
libc.src.math.exp10m1f16
libc.src.__support.FPUtil.cast
)

add_fp_unittest(
copysign_test
SUITE
Expand Down
113 changes: 113 additions & 0 deletions libc/test/src/math/smoke/exp10m1f16_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//===-- Unittests for exp10m1f16 ------------------------------------------===//
//
// 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/__support/FPUtil/cast.h"
#include "src/errno/libc_errno.h"
#include "src/math/exp10m1f16.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"

using LlvmLibcExp10m1f16Test = LIBC_NAMESPACE::testing::FPTest<float16>;

TEST_F(LlvmLibcExp10m1f16Test, SpecialNumbers) {
LIBC_NAMESPACE::libc_errno = 0;

EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::exp10m1f16(aNaN));
EXPECT_MATH_ERRNO(0);

EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::exp10m1f16(sNaN),
FE_INVALID);
EXPECT_MATH_ERRNO(0);

EXPECT_FP_EQ_ALL_ROUNDING(inf, LIBC_NAMESPACE::exp10m1f16(inf));
EXPECT_MATH_ERRNO(0);

EXPECT_FP_EQ_ALL_ROUNDING(LIBC_NAMESPACE::fputil::cast<float16>(-1.0),
LIBC_NAMESPACE::exp10m1f16(neg_inf));
EXPECT_MATH_ERRNO(0);

EXPECT_FP_EQ_ALL_ROUNDING(zero, LIBC_NAMESPACE::exp10m1f16(zero));
EXPECT_MATH_ERRNO(0);

EXPECT_FP_EQ_ALL_ROUNDING(neg_zero, LIBC_NAMESPACE::exp10m1f16(neg_zero));
EXPECT_MATH_ERRNO(0);
}

TEST_F(LlvmLibcExp10m1f16Test, Overflow) {
LIBC_NAMESPACE::libc_errno = 0;

EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::exp10m1f16(max_normal),
FE_OVERFLOW | FE_INEXACT);
EXPECT_MATH_ERRNO(ERANGE);

// round(16 * log10(2), HP, RN);
float16 x = LIBC_NAMESPACE::fputil::cast<float16>(0x1.344p+2);

EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_NEAREST(
inf, LIBC_NAMESPACE::exp10m1f16(x), FE_OVERFLOW | FE_INEXACT);
EXPECT_MATH_ERRNO(ERANGE);

EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_UPWARD(
inf, LIBC_NAMESPACE::exp10m1f16(x), FE_OVERFLOW | FE_INEXACT);
EXPECT_MATH_ERRNO(ERANGE);

EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_DOWNWARD(
max_normal, LIBC_NAMESPACE::exp10m1f16(x), FE_INEXACT);
EXPECT_MATH_ERRNO(0);

EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_TOWARD_ZERO(
max_normal, LIBC_NAMESPACE::exp10m1f16(x), FE_INEXACT);
EXPECT_MATH_ERRNO(0);
}

TEST_F(LlvmLibcExp10m1f16Test, ResultNearNegOne) {
LIBC_NAMESPACE::libc_errno = 0;

EXPECT_FP_EQ_WITH_EXCEPTION(LIBC_NAMESPACE::fputil::cast<float16>(-1.0),
LIBC_NAMESPACE::exp10m1f16(neg_max_normal),
FE_INEXACT);

// round(-11 * log10(2), HP, RD);
float16 x = LIBC_NAMESPACE::fputil::cast<float16>(-0x1.a8p+1);

EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_NEAREST(
LIBC_NAMESPACE::fputil::cast<float16>(-0x1.ffcp-1),
LIBC_NAMESPACE::exp10m1f16(x), FE_INEXACT);

EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_UPWARD(
LIBC_NAMESPACE::fputil::cast<float16>(-0x1.ffcp-1),
LIBC_NAMESPACE::exp10m1f16(x), FE_INEXACT);

EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_DOWNWARD(
LIBC_NAMESPACE::fputil::cast<float16>(-1.0),
LIBC_NAMESPACE::exp10m1f16(x), FE_INEXACT);

EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_TOWARD_ZERO(
LIBC_NAMESPACE::fputil::cast<float16>(-0x1.ffcp-1),
LIBC_NAMESPACE::exp10m1f16(x), FE_INEXACT);

// Next float16 value below -0x1.ce4p+1.
x = LIBC_NAMESPACE::fputil::cast<float16>(-0x1.ce8p+1);

EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_NEAREST(
LIBC_NAMESPACE::fputil::cast<float16>(-1.0),
LIBC_NAMESPACE::exp10m1f16(x), FE_INEXACT);

EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_UPWARD(
LIBC_NAMESPACE::fputil::cast<float16>(-0x1.ffcp-1),
LIBC_NAMESPACE::exp10m1f16(x), FE_INEXACT);

EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_DOWNWARD(
LIBC_NAMESPACE::fputil::cast<float16>(-1.0),
LIBC_NAMESPACE::exp10m1f16(x), FE_INEXACT);

EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_TOWARD_ZERO(
LIBC_NAMESPACE::fputil::cast<float16>(-0x1.ffcp-1),
LIBC_NAMESPACE::exp10m1f16(x), FE_INEXACT);
}
25 changes: 25 additions & 0 deletions libc/utils/MPFRWrapper/MPFRUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,29 @@ class MPFRNumber {
return result;
}

MPFRNumber exp10m1() const {
// TODO: Only use mpfr_exp10m1 once CI and buildbots get MPFR >= 4.2.0.
#if MPFR_VERSION_MAJOR > 4 || \
(MPFR_VERSION_MAJOR == 4 && MPFR_VERSION_MINOR >= 2)
MPFRNumber result(*this);
mpfr_exp10m1(result.value, value, mpfr_rounding);
return result;
#else
unsigned int prec = mpfr_precision * 3;
MPFRNumber result(*this, prec);

MPFRNumber ln10(10.0f, prec);
// log(10)
mpfr_log(ln10.value, ln10.value, mpfr_rounding);
// x * log(10)
mpfr_mul(result.value, value, ln10.value, mpfr_rounding);
// e^(x * log(10)) - 1
int ex = mpfr_expm1(result.value, result.value, mpfr_rounding);
mpfr_subnormalize(result.value, ex, mpfr_rounding);
return result;
#endif
}

MPFRNumber expm1() const {
MPFRNumber result(*this);
mpfr_expm1(result.value, value, mpfr_rounding);
Expand Down Expand Up @@ -744,6 +767,8 @@ unary_operation(Operation op, InputType input, unsigned int precision,
return mpfrInput.exp2m1();
case Operation::Exp10:
return mpfrInput.exp10();
case Operation::Exp10m1:
return mpfrInput.exp10m1();
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 @@ -42,6 +42,7 @@ enum class Operation : int {
Exp2,
Exp2m1,
Exp10,
Exp10m1,
Expm1,
Floor,
Log,
Expand Down