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

#include "src/__support/macros/config.h"
#include "src/__support/macros/properties/types.h"

namespace LIBC_NAMESPACE_DECL {

float16 exp2m1f16(float16 x);

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC_MATH_EXP2M1F16_H
184 changes: 180 additions & 4 deletions libc/src/math/generic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1441,13 +1441,9 @@ add_entrypoint_object(
.expxf16
libc.hdr.errno_macros
libc.hdr.fenv_macros
libc.src.__support.CPP.array
libc.src.__support.FPUtil.except_value_utils
libc.src.__support.FPUtil.fenv_impl
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.multiply_add
libc.src.__support.FPUtil.nearest_integer
libc.src.__support.FPUtil.polyeval
libc.src.__support.FPUtil.rounding_mode
libc.src.__support.macros.optimization
COMPILE_OPTIONS
Expand Down Expand Up @@ -1475,6 +1471,28 @@ add_entrypoint_object(
-O3
)

add_entrypoint_object(
exp2m1f16
SRCS
exp2m1f16.cpp
HDRS
../exp2m1f16.h
DEPENDS
.expxf16
libc.hdr.errno_macros
libc.hdr.fenv_macros
libc.src.__support.common
libc.src.__support.FPUtil.except_value_utils
libc.src.__support.FPUtil.fenv_impl
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.multiply_add
libc.src.__support.FPUtil.polyeval
libc.src.__support.FPUtil.rounding_mode
libc.src.__support.macros.optimization
COMPILE_OPTIONS
-O3
)

add_entrypoint_object(
exp10
SRCS
Expand Down Expand Up @@ -1558,6 +1576,28 @@ add_entrypoint_object(
-O3
)

add_entrypoint_object(
exp10m1f16
SRCS
exp10m1f16.cpp
HDRS
../exp10m1f16.h
DEPENDS
.expxf16
libc.hdr.errno_macros
libc.hdr.fenv_macros
libc.src.__support.FPUtil.except_value_utils
libc.src.__support.FPUtil.fenv_impl
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.multiply_add
libc.src.__support.FPUtil.polyeval
libc.src.__support.FPUtil.rounding_mode
libc.src.__support.macros.optimization
libc.src.__support.macros.properties.cpu_features
COMPILE_OPTIONS
-O3
)

add_entrypoint_object(
expm1
SRCS
Expand Down Expand Up @@ -2054,6 +2094,27 @@ add_entrypoint_object(
-O3
)

add_entrypoint_object(
log10f16
SRCS
log10f16.cpp
HDRS
../log10f16.h
DEPENDS
.expxf16
libc.hdr.errno_macros
libc.hdr.fenv_macros
libc.src.__support.FPUtil.except_value_utils
libc.src.__support.FPUtil.fenv_impl
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.multiply_add
libc.src.__support.FPUtil.polyeval
libc.src.__support.macros.optimization
libc.src.__support.macros.properties.cpu_features
COMPILE_OPTIONS
-O3
)

add_entrypoint_object(
log1p
SRCS
Expand Down Expand Up @@ -2131,6 +2192,27 @@ add_entrypoint_object(
-O3
)

add_entrypoint_object(
log2f16
SRCS
log2f16.cpp
HDRS
../log2f16.h
DEPENDS
.expxf16
libc.hdr.errno_macros
libc.hdr.fenv_macros
libc.src.__support.FPUtil.except_value_utils
libc.src.__support.FPUtil.fenv_impl
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.multiply_add
libc.src.__support.FPUtil.polyeval
libc.src.__support.macros.optimization
libc.src.__support.macros.properties.cpu_features
COMPILE_OPTIONS
-O3
)

add_entrypoint_object(
log
SRCS
Expand Down Expand Up @@ -2170,6 +2252,28 @@ add_entrypoint_object(
-O3
)

add_entrypoint_object(
logf16
SRCS
logf16.cpp
HDRS
../logf16.h
DEPENDS
.expxf16
libc.hdr.errno_macros
libc.hdr.fenv_macros
libc.src.__support.CPP.array
libc.src.__support.FPUtil.except_value_utils
libc.src.__support.FPUtil.fenv_impl
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.multiply_add
libc.src.__support.FPUtil.polyeval
libc.src.__support.macros.optimization
libc.src.__support.macros.properties.cpu_features
COMPILE_OPTIONS
-O3
)

add_entrypoint_object(
logb
SRCS
Expand Down Expand Up @@ -3062,6 +3166,18 @@ add_entrypoint_object(
-O3
)

add_entrypoint_object(
sqrtf16
SRCS
sqrtf16.cpp
HDRS
../sqrtf16.h
DEPENDS
libc.src.__support.FPUtil.sqrt
COMPILE_OPTIONS
-O3
)

add_entrypoint_object(
sqrtf128
SRCS
Expand Down Expand Up @@ -4044,6 +4160,25 @@ add_entrypoint_object(
-O3
)

add_entrypoint_object(
coshf16
SRCS
coshf16.cpp
HDRS
../coshf16.h
DEPENDS
.expxf16
libc.hdr.errno_macros
libc.hdr.fenv_macros
libc.src.__support.FPUtil.except_value_utils
libc.src.__support.FPUtil.fenv_impl
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.rounding_mode
libc.src.__support.macros.optimization
COMPILE_OPTIONS
-O3
)

add_entrypoint_object(
sinhf
SRCS
Expand All @@ -4059,6 +4194,25 @@ add_entrypoint_object(
-O3
)

add_entrypoint_object(
sinhf16
SRCS
sinhf16.cpp
HDRS
../sinhf16.h
DEPENDS
.expxf16
libc.hdr.errno_macros
libc.hdr.fenv_macros
libc.src.__support.FPUtil.except_value_utils
libc.src.__support.FPUtil.fenv_impl
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.rounding_mode
libc.src.__support.macros.optimization
COMPILE_OPTIONS
-O3
)

add_entrypoint_object(
tanhf
SRCS
Expand All @@ -4076,6 +4230,28 @@ add_entrypoint_object(
-O3
)

add_entrypoint_object(
tanhf16
SRCS
tanhf16.cpp
HDRS
../tanhf16.h
DEPENDS
.expxf16
libc.hdr.fenv_macros
libc.src.__support.CPP.array
libc.src.__support.FPUtil.except_value_utils
libc.src.__support.FPUtil.fenv_impl
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.multiply_add
libc.src.__support.FPUtil.nearest_integer
libc.src.__support.FPUtil.polyeval
libc.src.__support.FPUtil.rounding_mode
libc.src.__support.macros.optimization
COMPILE_OPTIONS
-O3
)

add_entrypoint_object(
acoshf
SRCS
Expand Down
103 changes: 103 additions & 0 deletions libc/src/math/generic/coshf16.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//===-- Half-precision cosh(x) function -----------------------------------===//
//
// 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/coshf16.h"
#include "expxf16.h"
#include "hdr/errno_macros.h"
#include "hdr/fenv_macros.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/except_value_utils.h"
#include "src/__support/FPUtil/rounding_mode.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h"

namespace LIBC_NAMESPACE_DECL {

static constexpr fputil::ExceptValues<float16, 9> COSHF16_EXCEPTS_POS = {{
// x = 0x1.6ap-5, coshf16(x) = 0x1p+0 (RZ)
{0x29a8U, 0x3c00U, 1U, 0U, 1U},
// x = 0x1.8c4p+0, coshf16(x) = 0x1.3a8p+1 (RZ)
{0x3e31U, 0x40eaU, 1U, 0U, 0U},
// x = 0x1.994p+0, coshf16(x) = 0x1.498p+1 (RZ)
{0x3e65U, 0x4126U, 1U, 0U, 0U},
// x = 0x1.b6p+0, coshf16(x) = 0x1.6d8p+1 (RZ)
{0x3ed8U, 0x41b6U, 1U, 0U, 1U},
// x = 0x1.aap+1, coshf16(x) = 0x1.be8p+3 (RZ)
{0x42a8U, 0x4afaU, 1U, 0U, 1U},
// x = 0x1.cc4p+1, coshf16(x) = 0x1.23cp+4 (RZ)
{0x4331U, 0x4c8fU, 1U, 0U, 0U},
// x = 0x1.288p+2, coshf16(x) = 0x1.9b4p+5 (RZ)
{0x44a2U, 0x526dU, 1U, 0U, 0U},
// x = 0x1.958p+2, coshf16(x) = 0x1.1a4p+8 (RZ)
{0x4656U, 0x5c69U, 1U, 0U, 0U},
// x = 0x1.5fp+3, coshf16(x) = 0x1.c54p+14 (RZ)
{0x497cU, 0x7715U, 1U, 0U, 1U},
}};

static constexpr fputil::ExceptValues<float16, 4> COSHF16_EXCEPTS_NEG = {{
// x = -0x1.6ap-5, coshf16(x) = 0x1p+0 (RZ)
{0xa9a8U, 0x3c00U, 1U, 0U, 1U},
// x = -0x1.b6p+0, coshf16(x) = 0x1.6d8p+1 (RZ)
{0xbed8U, 0x41b6U, 1U, 0U, 1U},
// x = -0x1.288p+2, coshf16(x) = 0x1.9b4p+5 (RZ)
{0xc4a2U, 0x526dU, 1U, 0U, 0U},
// x = -0x1.5fp+3, coshf16(x) = 0x1.c54p+14 (RZ)
{0xc97cU, 0x7715U, 1U, 0U, 1U},
}};

LLVM_LIBC_FUNCTION(float16, coshf16, (float16 x)) {
using FPBits = fputil::FPBits<float16>;
FPBits x_bits(x);

uint16_t x_u = x_bits.uintval();
uint16_t x_abs = x_u & 0x7fffU;

// When |x| >= acosh(2^16), or x is NaN.
if (LIBC_UNLIKELY(x_abs >= 0x49e5U)) {
// cosh(NaN) = NaN
if (x_bits.is_nan()) {
if (x_bits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

return x;
}

// When |x| >= acosh(2^16).
if (x_abs >= 0x49e5U) {
// cosh(+/-inf) = +inf
if (x_bits.is_inf())
return FPBits::inf().get_val();

switch (fputil::quick_get_round()) {
case FE_TONEAREST:
case FE_UPWARD:
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
return FPBits::inf().get_val();
default:
return FPBits::max_normal().get_val();
}
}
}

if (x_bits.is_pos()) {
if (auto r = COSHF16_EXCEPTS_POS.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
return r.value();
} else {
if (auto r = COSHF16_EXCEPTS_NEG.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
return r.value();
}

return eval_sinh_or_cosh</*IsSinh=*/false>(x);
}

} // namespace LIBC_NAMESPACE_DECL
45 changes: 2 additions & 43 deletions libc/src/math/generic/exp10f16.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,6 @@ static constexpr fputil::ExceptValues<float16, N_EXP10F16_EXCEPTS>
#endif
}};

// Generated by Sollya with the following commands:
// > display = hexadecimal;
// > round(log2(10), SG, RN);
static constexpr float LOG2F_10 = 0x1.a934fp+1f;

// Generated by Sollya with the following commands:
// > display = hexadecimal;
// > round(log10(2), SG, RN);
static constexpr float LOG10F_2 = 0x1.344136p-2f;

LLVM_LIBC_FUNCTION(float16, exp10f16, (float16 x)) {
using FPBits = fputil::FPBits<float16>;
FPBits x_bits(x);
Expand Down Expand Up @@ -131,39 +121,8 @@ LLVM_LIBC_FUNCTION(float16, exp10f16, (float16 x)) {
if (auto r = EXP10F16_EXCEPTS.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
return r.value();

// For -8 < x < 5, to compute 10^x, we perform the following range reduction:
// find hi, mid, lo, such that:
// x = (hi + mid) * log2(10) + lo, in which
// hi is an integer,
// mid * 2^3 is an integer,
// -2^(-4) <= lo < 2^(-4).
// In particular,
// hi + mid = round(x * 2^3) * 2^(-3).
// Then,
// 10^x = 10^(hi + mid + lo) = 2^((hi + mid) * log2(10)) + 10^lo
// We store 2^mid in the lookup table EXP2_MID_BITS, and compute 2^hi * 2^mid
// by adding hi to the exponent field of 2^mid. 10^lo is computed using a
// degree-4 minimax polynomial generated by Sollya.

float xf = x;
float kf = fputil::nearest_integer(xf * (LOG2F_10 * 0x1.0p+3f));
int x_hi_mid = static_cast<int>(kf);
int x_hi = x_hi_mid >> 3;
int x_mid = x_hi_mid & 0x7;
// lo = x - (hi + mid) = round(x * 2^3 * log2(10)) * log10(2) * (-2^(-3)) + x
float lo = fputil::multiply_add(kf, LOG10F_2 * -0x1.0p-3f, xf);

uint32_t exp2_hi_mid_bits =
EXP2_MID_BITS[x_mid] +
static_cast<uint32_t>(x_hi << fputil::FPBits<float>::FRACTION_LEN);
float exp2_hi_mid = fputil::FPBits<float>(exp2_hi_mid_bits).get_val();
// Degree-4 minimax polynomial generated by Sollya with the following
// commands:
// > display = hexadecimal;
// > P = fpminimax((10^x - 1)/x, 3, [|SG...|], [-2^-4, 2^-4]);
// > 1 + x * P;
float exp10_lo = fputil::polyeval(lo, 0x1p+0f, 0x1.26bb14p+1f, 0x1.53526p+1f,
0x1.04b434p+1f, 0x1.2bcf9ep+0f);
// 10^x = 2^((hi + mid) * log2(10)) * 10^lo
auto [exp2_hi_mid, exp10_lo] = exp10_range_reduction(x);
return static_cast<float16>(exp2_hi_mid * exp10_lo);
}

Expand Down
162 changes: 162 additions & 0 deletions libc/src/math/generic/exp10m1f16.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
//===-- Half-precision 10^x - 1 function ----------------------------------===//
//
// 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 "expxf16.h"
#include "hdr/errno_macros.h"
#include "hdr/fenv_macros.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/except_value_utils.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/FPUtil/rounding_mode.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h"
#include "src/__support/macros/properties/cpu_features.h"

namespace LIBC_NAMESPACE_DECL {

static constexpr fputil::ExceptValues<float16, 3> EXP10M1F16_EXCEPTS_LO = {{
// (input, RZ output, RU offset, RD offset, RN offset)
// x = 0x1.5c4p-4, exp10m1f16(x) = 0x1.bacp-3 (RZ)
{0x2d71U, 0x32ebU, 1U, 0U, 0U},
// x = -0x1.5ep-13, exp10m1f16(x) = -0x1.92cp-12 (RZ)
{0x8978U, 0x8e4bU, 0U, 1U, 0U},
// x = -0x1.e2p-10, exp10m1f16(x) = -0x1.14cp-8 (RZ)
{0x9788U, 0x9c53U, 0U, 1U, 0U},
}};

#ifdef LIBC_TARGET_CPU_HAS_FMA
static constexpr size_t N_EXP10M1F16_EXCEPTS_HI = 3;
#else
static constexpr size_t N_EXP10M1F16_EXCEPTS_HI = 6;
#endif

static constexpr fputil::ExceptValues<float16, N_EXP10M1F16_EXCEPTS_HI>
EXP10M1F16_EXCEPTS_HI = {{
// (input, RZ output, RU offset, RD offset, RN offset)
// x = 0x1.8f4p-2, exp10m1f16(x) = 0x1.744p+0 (RZ)
{0x363dU, 0x3dd1U, 1U, 0U, 0U},
// x = 0x1.95cp-2, exp10m1f16(x) = 0x1.7d8p+0 (RZ)
{0x3657U, 0x3df6U, 1U, 0U, 0U},
// x = 0x1.d04p-2, exp10m1f16(x) = 0x1.d7p+0 (RZ)
{0x3741U, 0x3f5cU, 1U, 0U, 1U},
#ifndef LIBC_TARGET_CPU_HAS_FMA
// x = 0x1.0cp+1, exp10m1f16(x) = 0x1.ec4p+6 (RZ)
{0x4030U, 0x57b1U, 1U, 0U, 1U},
// x = 0x1.1b8p+1, exp10m1f16(x) = 0x1.45cp+7 (RZ)
{0x406eU, 0x5917U, 1U, 0U, 1U},
// x = 0x1.2f4p+2, exp10m1f16(x) = 0x1.ab8p+15 (RZ)
{0x44bdU, 0x7aaeU, 1U, 0U, 1U},
#endif
}};

LLVM_LIBC_FUNCTION(float16, exp10m1f16, (float16 x)) {
using FPBits = fputil::FPBits<float16>;
FPBits x_bits(x);

uint16_t x_u = x_bits.uintval();
uint16_t x_abs = x_u & 0x7fffU;

// When |x| <= 2^(-3), or |x| >= 11 * log10(2), or x is NaN.
if (LIBC_UNLIKELY(x_abs <= 0x3000U || x_abs >= 0x429fU)) {
// exp10m1(NaN) = NaN
if (x_bits.is_nan()) {
if (x_bits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

return x;
}

// When x >= 16 * log10(2).
if (x_u >= 0x44d1U && x_bits.is_pos()) {
// exp10m1(+inf) = +inf
if (x_bits.is_inf())
return FPBits::inf().get_val();

switch (fputil::quick_get_round()) {
case FE_TONEAREST:
case FE_UPWARD:
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
return FPBits::inf().get_val();
default:
return FPBits::max_normal().get_val();
}
}

// When x < -11 * log10(2).
if (x_u > 0xc29fU) {
// exp10m1(-inf) = -1
if (x_bits.is_inf())
return FPBits::one(Sign::NEG).get_val();

// When x >= -0x1.ce4p+1, round(10^x - 1, HP, RN) = -0x1.ffcp-1.
if (x_u <= 0xc339U) {
return fputil::round_result_slightly_down(
static_cast<float16>(-0x1.ffcp-1));
}

// When x < -0x1.ce4p+1, round(10^x - 1, HP, RN) = -1.
switch (fputil::quick_get_round()) {
case FE_TONEAREST:
case FE_DOWNWARD:
return FPBits::one(Sign::NEG).get_val();
default:
return static_cast<float16>(-0x1.ffcp-1);
}
}

// When |x| <= 2^(-3).
if (x_abs <= 0x3000U) {
if (auto r = EXP10M1F16_EXCEPTS_LO.lookup(x_u);
LIBC_UNLIKELY(r.has_value()))
return r.value();

float xf = x;
// Degree-5 minimax polynomial generated by Sollya with the following
// commands:
// > display = hexadecimal;
// > P = fpminimax((10^x - 1)/x, 4, [|SG...|], [-2^-3, 2^-3]);
// > x * P;
return static_cast<float16>(
xf * fputil::polyeval(xf, 0x1.26bb1cp+1f, 0x1.5351c8p+1f,
0x1.04704p+1f, 0x1.2ce084p+0f, 0x1.14a6bep-1f));
}
}

// When x is 1, 2, or 3. These are hard-to-round cases with exact results.
// 10^4 - 1 = 9'999 is not exactly representable as a float16, but luckily the
// polynomial approximation gives the correct result for x = 4 in all
// rounding modes.
if (LIBC_UNLIKELY((x_u & ~(0x3c00U | 0x4000U | 0x4200U | 0x4400U)) == 0)) {
switch (x_u) {
case 0x3c00U: // x = 1.0f16
return static_cast<float16>(9.0);
case 0x4000U: // x = 2.0f16
return static_cast<float16>(99.0);
case 0x4200U: // x = 3.0f16
return static_cast<float16>(999.0);
}
}

if (auto r = EXP10M1F16_EXCEPTS_HI.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
return r.value();

// exp10(x) = exp2((hi + mid) * log2(10)) * exp10(lo)
auto [exp2_hi_mid, exp10_lo] = exp10_range_reduction(x);
// exp10m1(x) = exp2((hi + mid) * log2(lo)) * exp10(lo) - 1
return static_cast<float16>(
fputil::multiply_add(exp2_hi_mid, exp10_lo, -1.0f));
}

} // namespace LIBC_NAMESPACE_DECL
39 changes: 2 additions & 37 deletions libc/src/math/generic/exp2f16.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,9 @@
#include "expxf16.h"
#include "hdr/errno_macros.h"
#include "hdr/fenv_macros.h"
#include "src/__support/CPP/array.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/except_value_utils.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/FPUtil/nearest_integer.h"
#include "src/__support/FPUtil/rounding_mode.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
Expand Down Expand Up @@ -88,39 +84,8 @@ LLVM_LIBC_FUNCTION(float16, exp2f16, (float16 x)) {
if (auto r = EXP2F16_EXCEPTS.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
return r.value();

// For -25 < x < 16, to compute 2^x, we perform the following range reduction:
// find hi, mid, lo, such that:
// x = hi + mid + lo, in which
// hi is an integer,
// mid * 2^3 is an integer,
// -2^(-4) <= lo < 2^(-4).
// In particular,
// hi + mid = round(x * 2^3) * 2^(-3).
// Then,
// 2^x = 2^(hi + mid + lo) = 2^hi * 2^mid * 2^lo.
// We store 2^mid in the lookup table EXP2_MID_BITS, and compute 2^hi * 2^mid
// by adding hi to the exponent field of 2^mid. 2^lo is computed using a
// degree-3 minimax polynomial generated by Sollya.

float xf = x;
float kf = fputil::nearest_integer(xf * 0x1.0p+3f);
int x_hi_mid = static_cast<int>(kf);
int x_hi = x_hi_mid >> 3;
int x_mid = x_hi_mid & 0x7;
// lo = x - (hi + mid) = round(x * 2^3) * (-2^(-3)) + x
float lo = fputil::multiply_add(kf, -0x1.0p-3f, xf);

uint32_t exp2_hi_mid_bits =
EXP2_MID_BITS[x_mid] +
static_cast<uint32_t>(x_hi << fputil::FPBits<float>::FRACTION_LEN);
float exp2_hi_mid = fputil::FPBits<float>(exp2_hi_mid_bits).get_val();
// Degree-3 minimax polynomial generated by Sollya with the following
// commands:
// > display = hexadecimal;
// > P = fpminimax((2^x - 1)/x, 2, [|SG...|], [-2^-4, 2^-4]);
// > 1 + x * P;
float exp2_lo = fputil::polyeval(lo, 0x1p+0f, 0x1.62e43p-1f, 0x1.ec0aa6p-3f,
0x1.c6b4a6p-5f);
// exp2(x) = exp2(hi + mid) * exp2(lo)
auto [exp2_hi_mid, exp2_lo] = exp2_range_reduction(x);
return static_cast<float16>(exp2_hi_mid * exp2_lo);
}

Expand Down
161 changes: 161 additions & 0 deletions libc/src/math/generic/exp2m1f16.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
//===-- Half-precision 2^x - 1 function -----------------------------------===//
//
// 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/exp2m1f16.h"
#include "expxf16.h"
#include "hdr/errno_macros.h"
#include "hdr/fenv_macros.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/except_value_utils.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/FPUtil/rounding_mode.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h"

namespace LIBC_NAMESPACE_DECL {

static constexpr fputil::ExceptValues<float16, 6> EXP2M1F16_EXCEPTS_LO = {{
// (input, RZ output, RU offset, RD offset, RN offset)
// x = 0x1.cf4p-13, exp2m1f16(x) = 0x1.41p-13 (RZ)
{0x0b3dU, 0x0904U, 1U, 0U, 1U},
// x = 0x1.4fcp-12, exp2m1f16(x) = 0x1.d14p-13 (RZ)
{0x0d3fU, 0x0b45U, 1U, 0U, 1U},
// x = 0x1.63p-11, exp2m1f16(x) = 0x1.ec4p-12 (RZ)
{0x118cU, 0x0fb1U, 1U, 0U, 0U},
// x = 0x1.6fp-7, exp2m1f16(x) = 0x1.fe8p-8 (RZ)
{0x21bcU, 0x1ffaU, 1U, 0U, 1U},
// x = -0x1.c6p-10, exp2m1f16(x) = -0x1.3a8p-10 (RZ)
{0x9718U, 0x94eaU, 0U, 1U, 0U},
// x = -0x1.cfcp-10, exp2m1f16(x) = -0x1.414p-10 (RZ)
{0x973fU, 0x9505U, 0U, 1U, 0U},
}};

#ifdef LIBC_TARGET_CPU_HAS_FMA
static constexpr size_t N_EXP2M1F16_EXCEPTS_HI = 6;
#else
static constexpr size_t N_EXP2M1F16_EXCEPTS_HI = 7;
#endif

static constexpr fputil::ExceptValues<float16, N_EXP2M1F16_EXCEPTS_HI>
EXP2M1F16_EXCEPTS_HI = {{
// (input, RZ output, RU offset, RD offset, RN offset)
// x = 0x1.e58p-3, exp2m1f16(x) = 0x1.6dcp-3 (RZ)
{0x3396U, 0x31b7U, 1U, 0U, 0U},
#ifndef LIBC_TARGET_CPU_HAS_FMA
// x = 0x1.2e8p-2, exp2m1f16(x) = 0x1.d14p-3 (RZ)
{0x34baU, 0x3345U, 1U, 0U, 0U},
#endif
// x = 0x1.ad8p-2, exp2m1f16(x) = 0x1.598p-2 (RZ)
{0x36b6U, 0x3566U, 1U, 0U, 0U},
#ifdef LIBC_TARGET_CPU_HAS_FMA
// x = 0x1.edcp-2, exp2m1f16(x) = 0x1.964p-2 (RZ)
{0x37b7U, 0x3659U, 1U, 0U, 1U},
#endif
// x = -0x1.804p-3, exp2m1f16(x) = -0x1.f34p-4 (RZ)
{0xb201U, 0xafcdU, 0U, 1U, 1U},
// x = -0x1.f3p-3, exp2m1f16(x) = -0x1.3e4p-3 (RZ)
{0xb3ccU, 0xb0f9U, 0U, 1U, 0U},
// x = -0x1.294p-1, exp2m1f16(x) = -0x1.53p-2 (RZ)
{0xb8a5U, 0xb54cU, 0U, 1U, 1U},
#ifndef LIBC_TARGET_CPU_HAS_FMA
// x = -0x1.a34p-1, exp2m1f16(x) = -0x1.bb4p-2 (RZ)
{0xba8dU, 0xb6edU, 0U, 1U, 1U},
#endif
}};

LLVM_LIBC_FUNCTION(float16, exp2m1f16, (float16 x)) {
using FPBits = fputil::FPBits<float16>;
FPBits x_bits(x);

uint16_t x_u = x_bits.uintval();
uint16_t x_abs = x_u & 0x7fffU;

// When |x| <= 2^(-3), or |x| >= 11, or x is NaN.
if (LIBC_UNLIKELY(x_abs <= 0x3000U || x_abs >= 0x4980U)) {
// exp2m1(NaN) = NaN
if (x_bits.is_nan()) {
if (x_bits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

return x;
}

// When x >= 16.
if (x_u >= 0x4c00 && x_bits.is_pos()) {
// exp2m1(+inf) = +inf
if (x_bits.is_inf())
return FPBits::inf().get_val();

switch (fputil::quick_get_round()) {
case FE_TONEAREST:
case FE_UPWARD:
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
return FPBits::inf().get_val();
default:
return FPBits::max_normal().get_val();
}
}

// When x < -11.
if (x_u > 0xc980U) {
// exp2m1(-inf) = -1
if (x_bits.is_inf())
return FPBits::one(Sign::NEG).get_val();

// When -12 < x < -11, round(2^x - 1, HP, RN) = -0x1.ffcp-1.
if (x_u < 0xca00U) {
return fputil::round_result_slightly_down(
static_cast<float16>(-0x1.ffcp-1));
}

// When x <= -12, round(2^x - 1, HP, RN) = -1.
switch (fputil::quick_get_round()) {
case FE_TONEAREST:
case FE_DOWNWARD:
return FPBits::one(Sign::NEG).get_val();
default:
return static_cast<float16>(-0x1.ffcp-1);
}
}

// When |x| <= 2^(-3).
if (x_abs <= 0x3000U) {
if (auto r = EXP2M1F16_EXCEPTS_LO.lookup(x_u);
LIBC_UNLIKELY(r.has_value()))
return r.value();

float xf = x;
// Degree-5 minimax polynomial generated by Sollya with the following
// commands:
// > display = hexadecimal;
// > P = fpminimax((2^x - 1)/x, 4, [|SG...|], [-2^-3, 2^-3]);
// > x * P;
return static_cast<float16>(
xf * fputil::polyeval(xf, 0x1.62e43p-1f, 0x1.ebfbdep-3f,
0x1.c6af88p-5f, 0x1.3b45d6p-7f,
0x1.641e7cp-10f));
}
}

if (auto r = EXP2M1F16_EXCEPTS_HI.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
return r.value();

// exp2(x) = exp2(hi + mid) * exp2(lo)
auto [exp2_hi_mid, exp2_lo] = exp2_range_reduction(x);
// exp2m1(x) = exp2(hi + mid) * exp2(lo) - 1
return static_cast<float16>(
fputil::multiply_add(exp2_hi_mid, exp2_lo, -1.0f));
}

} // namespace LIBC_NAMESPACE_DECL
252 changes: 252 additions & 0 deletions libc/src/math/generic/expxf16.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define LLVM_LIBC_SRC_MATH_GENERIC_EXPXF16_H

#include "src/__support/CPP/array.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/FPUtil/nearest_integer.h"
Expand Down Expand Up @@ -89,6 +90,257 @@ constexpr cpp::array<uint32_t, 8> EXP2_MID_BITS = {
0x3fb5'04f3U, 0x3fc5'672aU, 0x3fd7'44fdU, 0x3fea'c0c7U,
};

LIBC_INLINE ExpRangeReduction exp2_range_reduction(float16 x) {
// For -25 < x < 16, to compute 2^x, we perform the following range reduction:
// find hi, mid, lo, such that:
// x = hi + mid + lo, in which
// hi is an integer,
// mid * 2^3 is an integer,
// -2^(-4) <= lo < 2^(-4).
// In particular,
// hi + mid = round(x * 2^3) * 2^(-3).
// Then,
// 2^x = 2^(hi + mid + lo) = 2^hi * 2^mid * 2^lo.
// We store 2^mid in the lookup table EXP2_MID_BITS, and compute 2^hi * 2^mid
// by adding hi to the exponent field of 2^mid. 2^lo is computed using a
// degree-3 minimax polynomial generated by Sollya.

float xf = x;
float kf = fputil::nearest_integer(xf * 0x1.0p+3f);
int x_hi_mid = static_cast<int>(kf);
int x_hi = x_hi_mid >> 3;
int x_mid = x_hi_mid & 0x7;
// lo = x - (hi + mid) = round(x * 2^3) * (-2^(-3)) + x
float lo = fputil::multiply_add(kf, -0x1.0p-3f, xf);

uint32_t exp2_hi_mid_bits =
EXP2_MID_BITS[x_mid] +
static_cast<uint32_t>(x_hi << fputil::FPBits<float>::FRACTION_LEN);
float exp2_hi_mid = fputil::FPBits<float>(exp2_hi_mid_bits).get_val();
// Degree-3 minimax polynomial generated by Sollya with the following
// commands:
// > display = hexadecimal;
// > P = fpminimax((2^x - 1)/x, 2, [|SG...|], [-2^-4, 2^-4]);
// > 1 + x * P;
float exp2_lo = fputil::polyeval(lo, 0x1p+0f, 0x1.62e43p-1f, 0x1.ec0aa6p-3f,
0x1.c6b4a6p-5f);
return {exp2_hi_mid, exp2_lo};
}

// Generated by Sollya with the following commands:
// > display = hexadecimal;
// > round(log2(10), SG, RN);
static constexpr float LOG2F_10 = 0x1.a934fp+1f;

// Generated by Sollya with the following commands:
// > display = hexadecimal;
// > round(log10(2), SG, RN);
static constexpr float LOG10F_2 = 0x1.344136p-2f;

LIBC_INLINE ExpRangeReduction exp10_range_reduction(float16 x) {
// For -8 < x < 5, to compute 10^x, we perform the following range reduction:
// find hi, mid, lo, such that:
// x = (hi + mid) * log2(10) + lo, in which
// hi is an integer,
// mid * 2^3 is an integer,
// -2^(-4) <= lo < 2^(-4).
// In particular,
// hi + mid = round(x * 2^3) * 2^(-3).
// Then,
// 10^x = 10^(hi + mid + lo) = 2^((hi + mid) * log2(10)) + 10^lo
// We store 2^mid in the lookup table EXP2_MID_BITS, and compute 2^hi * 2^mid
// by adding hi to the exponent field of 2^mid. 10^lo is computed using a
// degree-4 minimax polynomial generated by Sollya.

float xf = x;
float kf = fputil::nearest_integer(xf * (LOG2F_10 * 0x1.0p+3f));
int x_hi_mid = static_cast<int>(kf);
int x_hi = x_hi_mid >> 3;
int x_mid = x_hi_mid & 0x7;
// lo = x - (hi + mid) = round(x * 2^3 * log2(10)) * log10(2) * (-2^(-3)) + x
float lo = fputil::multiply_add(kf, LOG10F_2 * -0x1.0p-3f, xf);

uint32_t exp2_hi_mid_bits =
EXP2_MID_BITS[x_mid] +
static_cast<uint32_t>(x_hi << fputil::FPBits<float>::FRACTION_LEN);
float exp2_hi_mid = fputil::FPBits<float>(exp2_hi_mid_bits).get_val();
// Degree-4 minimax polynomial generated by Sollya with the following
// commands:
// > display = hexadecimal;
// > P = fpminimax((10^x - 1)/x, 3, [|SG...|], [-2^-4, 2^-4]);
// > 1 + x * P;
float exp10_lo = fputil::polyeval(lo, 0x1p+0f, 0x1.26bb14p+1f, 0x1.53526p+1f,
0x1.04b434p+1f, 0x1.2bcf9ep+0f);
return {exp2_hi_mid, exp10_lo};
}

// Generated by Sollya with the following commands:
// > display = hexadecimal;
// > round(log2(exp(1)), SG, RN);
static constexpr float LOG2F_E = 0x1.715476p+0f;

// Generated by Sollya with the following commands:
// > display = hexadecimal;
// > round(log(2), SG, RN);
static constexpr float LOGF_2 = 0x1.62e43p-1f;

// Generated by Sollya with the following commands:
// > display = hexadecimal;
// > for i from 0 to 31 do printsingle(round(2^(i * 2^-5), SG, RN));
static constexpr cpp::array<uint32_t, 32> EXP2_MID_5_BITS = {
0x3f80'0000U, 0x3f82'cd87U, 0x3f85'aac3U, 0x3f88'980fU, 0x3f8b'95c2U,
0x3f8e'a43aU, 0x3f91'c3d3U, 0x3f94'f4f0U, 0x3f98'37f0U, 0x3f9b'8d3aU,
0x3f9e'f532U, 0x3fa2'7043U, 0x3fa5'fed7U, 0x3fa9'a15bU, 0x3fad'583fU,
0x3fb1'23f6U, 0x3fb5'04f3U, 0x3fb8'fbafU, 0x3fbd'08a4U, 0x3fc1'2c4dU,
0x3fc5'672aU, 0x3fc9'b9beU, 0x3fce'248cU, 0x3fd2'a81eU, 0x3fd7'44fdU,
0x3fdb'fbb8U, 0x3fe0'ccdfU, 0x3fe5'b907U, 0x3fea'c0c7U, 0x3fef'e4baU,
0x3ff5'257dU, 0x3ffa'83b3U,
};

// This function correctly calculates sinh(x) and cosh(x) by calculating exp(x)
// and exp(-x) simultaneously.
// To compute e^x, we perform the following range reduction:
// find hi, mid, lo such that:
// x = (hi + mid) * log(2) + lo, in which
// hi is an integer,
// 0 <= mid * 2^5 < 32 is an integer
// -2^(-5) <= lo * log2(e) <= 2^-5.
// In particular,
// hi + mid = round(x * log2(e) * 2^5) * 2^(-5).
// Then,
// e^x = 2^(hi + mid) * e^lo = 2^hi * 2^mid * e^lo.
// We store 2^mid in the lookup table EXP2_MID_5_BITS, and compute 2^hi * 2^mid
// by adding hi to the exponent field of 2^mid.
// e^lo is computed using a degree-3 minimax polynomial generated by Sollya:
// e^lo ~ P(lo)
// = 1 + lo + c2 * lo^2 + ... + c5 * lo^5
// = (1 + c2*lo^2 + c4*lo^4) + lo * (1 + c3*lo^2 + c5*lo^4)
// = P_even + lo * P_odd
// To compute e^(-x), notice that:
// e^(-x) = 2^(-(hi + mid)) * e^(-lo)
// ~ 2^(-(hi + mid)) * P(-lo)
// = 2^(-(hi + mid)) * (P_even - lo * P_odd)
// So:
// sinh(x) = (e^x - e^(-x)) / 2
// ~ 0.5 * (2^(hi + mid) * (P_even + lo * P_odd) -
// 2^(-(hi + mid)) * (P_even - lo * P_odd))
// = 0.5 * (P_even * (2^(hi + mid) - 2^(-(hi + mid))) +
// lo * P_odd * (2^(hi + mid) + 2^(-(hi + mid))))
// And similarly:
// cosh(x) = (e^x + e^(-x)) / 2
// ~ 0.5 * (P_even * (2^(hi + mid) + 2^(-(hi + mid))) +
// lo * P_odd * (2^(hi + mid) - 2^(-(hi + mid))))
// The main point of these formulas is that the expensive part of calculating
// the polynomials approximating lower parts of e^x and e^(-x) is shared and
// only done once.
template <bool IsSinh> LIBC_INLINE float16 eval_sinh_or_cosh(float16 x) {
float xf = x;
float kf = fputil::nearest_integer(xf * (LOG2F_E * 0x1.0p+5f));
int x_hi_mid_p = static_cast<int>(kf);
int x_hi_mid_m = -x_hi_mid_p;

int x_hi_p = x_hi_mid_p >> 5;
int x_hi_m = x_hi_mid_m >> 5;
int x_mid_p = x_hi_mid_p & 0x1f;
int x_mid_m = x_hi_mid_m & 0x1f;

uint32_t exp2_hi_mid_bits_p =
EXP2_MID_5_BITS[x_mid_p] +
static_cast<uint32_t>(x_hi_p << fputil::FPBits<float>::FRACTION_LEN);
uint32_t exp2_hi_mid_bits_m =
EXP2_MID_5_BITS[x_mid_m] +
static_cast<uint32_t>(x_hi_m << fputil::FPBits<float>::FRACTION_LEN);
// exp2_hi_mid_p = 2^(hi + mid)
float exp2_hi_mid_p = fputil::FPBits<float>(exp2_hi_mid_bits_p).get_val();
// exp2_hi_mid_m = 2^(-(hi + mid))
float exp2_hi_mid_m = fputil::FPBits<float>(exp2_hi_mid_bits_m).get_val();

// exp2_hi_mid_sum = 2^(hi + mid) + 2^(-(hi + mid))
float exp2_hi_mid_sum = exp2_hi_mid_p + exp2_hi_mid_m;
// exp2_hi_mid_diff = 2^(hi + mid) - 2^(-(hi + mid))
float exp2_hi_mid_diff = exp2_hi_mid_p - exp2_hi_mid_m;

// lo = x - (hi + mid) = round(x * log2(e) * 2^5) * log(2) * (-2^(-5)) + x
float lo = fputil::multiply_add(kf, LOGF_2 * -0x1.0p-5f, xf);
float lo_sq = lo * lo;

// Degree-3 minimax polynomial generated by Sollya with the following
// commands:
// > display = hexadecimal;
// > P = fpminimax(expm1(x)/x, 2, [|SG...|], [-2^-5, 2^-5]);
// > 1 + x * P;
constexpr cpp::array<float, 4> COEFFS = {0x1p+0f, 0x1p+0f, 0x1.0004p-1f,
0x1.555778p-3f};
float half_p_odd =
fputil::polyeval(lo_sq, COEFFS[1] * 0.5f, COEFFS[3] * 0.5f);
float half_p_even =
fputil::polyeval(lo_sq, COEFFS[0] * 0.5f, COEFFS[2] * 0.5f);

// sinh(x) = lo * (0.5 * P_odd * (2^(hi + mid) + 2^(-(hi + mid)))) +
// (0.5 * P_even * (2^(hi + mid) - 2^(-(hi + mid))))
if constexpr (IsSinh)
return static_cast<float16>(fputil::multiply_add(
lo, half_p_odd * exp2_hi_mid_sum, half_p_even * exp2_hi_mid_diff));
// cosh(x) = lo * (0.5 * P_odd * (2^(hi + mid) - 2^(-(hi + mid)))) +
// (0.5 * P_even * (2^(hi + mid) + 2^(-(hi + mid))))
return static_cast<float16>(fputil::multiply_add(
lo, half_p_odd * exp2_hi_mid_diff, half_p_even * exp2_hi_mid_sum));
}

// Generated by Sollya with the following commands:
// > display = hexadecimal;
// > for i from 0 to 31 do print(round(log(1 + i * 2^-5), SG, RN));
constexpr cpp::array<float, 32> LOGF_F = {
0x0p+0, 0x1.f829bp-6, 0x1.f0a30cp-5, 0x1.6f0d28p-4, 0x1.e27076p-4,
0x1.29553p-3, 0x1.5ff308p-3, 0x1.9525aap-3, 0x1.c8ff7cp-3, 0x1.fb9186p-3,
0x1.1675cap-2, 0x1.2e8e2cp-2, 0x1.4618bcp-2, 0x1.5d1bdcp-2, 0x1.739d8p-2,
0x1.89a338p-2, 0x1.9f323ep-2, 0x1.b44f78p-2, 0x1.c8ff7cp-2, 0x1.dd46ap-2,
0x1.f128f6p-2, 0x1.02552ap-1, 0x1.0be72ep-1, 0x1.154c3ep-1, 0x1.1e85f6p-1,
0x1.2795e2p-1, 0x1.307d74p-1, 0x1.393e0ep-1, 0x1.41d8fep-1, 0x1.4a4f86p-1,
0x1.52a2d2p-1, 0x1.5ad404p-1,
};

// Generated by Sollya with the following commands:
// > display = hexadecimal;
// > for i from 0 to 31 do print(round(log2(1 + i * 2^-5), SG, RN));
constexpr cpp::array<float, 32> LOG2F_F = {
0x0p+0f, 0x1.6bad38p-5f, 0x1.663f7p-4f, 0x1.08c588p-3f,
0x1.5c01a4p-3f, 0x1.acf5e2p-3f, 0x1.fbc16cp-3f, 0x1.24407ap-2f,
0x1.49a784p-2f, 0x1.6e221cp-2f, 0x1.91bba8p-2f, 0x1.b47ecp-2f,
0x1.d6753ep-2f, 0x1.f7a856p-2f, 0x1.0c105p-1f, 0x1.1bf312p-1f,
0x1.2b8034p-1f, 0x1.3abb4p-1f, 0x1.49a784p-1f, 0x1.584822p-1f,
0x1.66a008p-1f, 0x1.74b1fep-1f, 0x1.82809ep-1f, 0x1.900e62p-1f,
0x1.9d5dap-1f, 0x1.aa709p-1f, 0x1.b74948p-1f, 0x1.c3e9cap-1f,
0x1.d053f6p-1f, 0x1.dc899ap-1f, 0x1.e88c6cp-1f, 0x1.f45e08p-1f,
};

// Generated by Sollya with the following commands:
// > display = hexadecimal;
// > for i from 0 to 31 do print(round(log10(1 + i * 2^-5), SG, RN));
constexpr cpp::array<float, 32> LOG10F_F = {
0x0p+0f, 0x1.b5e908p-7f, 0x1.af5f92p-6f, 0x1.3ed11ap-5f,
0x1.a30a9ep-5f, 0x1.02428cp-4f, 0x1.31b306p-4f, 0x1.5fe804p-4f,
0x1.8cf184p-4f, 0x1.b8de4ep-4f, 0x1.e3bc1ap-4f, 0x1.06cbd6p-3f,
0x1.1b3e72p-3f, 0x1.2f3b6ap-3f, 0x1.42c7e8p-3f, 0x1.55e8c6p-3f,
0x1.68a288p-3f, 0x1.7af974p-3f, 0x1.8cf184p-3f, 0x1.9e8e7cp-3f,
0x1.afd3e4p-3f, 0x1.c0c514p-3f, 0x1.d1653p-3f, 0x1.e1b734p-3f,
0x1.f1bdeep-3f, 0x1.00be06p-2f, 0x1.087a08p-2f, 0x1.101432p-2f,
0x1.178da6p-2f, 0x1.1ee778p-2f, 0x1.2622bp-2f, 0x1.2d404cp-2f,
};

// Generated by Sollya with the following commands:
// > display = hexadecimal;
// > for i from 0 to 31 do print(round(1 / (1 + i * 2^-5), SG, RN));
constexpr cpp::array<float, 32> ONE_OVER_F = {
0x1p+0, 0x1.f07c2p-1, 0x1.e1e1e2p-1, 0x1.d41d42p-1, 0x1.c71c72p-1,
0x1.bacf92p-1, 0x1.af286cp-1, 0x1.a41a42p-1, 0x1.99999ap-1, 0x1.8f9c18p-1,
0x1.861862p-1, 0x1.7d05f4p-1, 0x1.745d18p-1, 0x1.6c16c2p-1, 0x1.642c86p-1,
0x1.5c9882p-1, 0x1.555556p-1, 0x1.4e5e0ap-1, 0x1.47ae14p-1, 0x1.414142p-1,
0x1.3b13b2p-1, 0x1.3521dp-1, 0x1.2f684cp-1, 0x1.29e412p-1, 0x1.24924ap-1,
0x1.1f7048p-1, 0x1.1a7b96p-1, 0x1.15b1e6p-1, 0x1.111112p-1, 0x1.0c9714p-1,
0x1.08421p-1, 0x1.041042p-1,
};

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC_MATH_GENERIC_EXPXF16_H
163 changes: 163 additions & 0 deletions libc/src/math/generic/log10f16.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
//===-- Half-precision log10(x) function ----------------------------------===//
//
// 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/log10f16.h"
#include "expxf16.h"
#include "hdr/errno_macros.h"
#include "hdr/fenv_macros.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/except_value_utils.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h"
#include "src/__support/macros/properties/cpu_features.h"

namespace LIBC_NAMESPACE_DECL {

#ifdef LIBC_TARGET_CPU_HAS_FMA
static constexpr size_t N_LOG10F16_EXCEPTS = 11;
#else
static constexpr size_t N_LOG10F16_EXCEPTS = 17;
#endif

static constexpr fputil::ExceptValues<float16, N_LOG10F16_EXCEPTS>
LOG10F16_EXCEPTS = {{
// (input, RZ output, RU offset, RD offset, RN offset)
// x = 0x1.e3cp-3, log10f16(x) = -0x1.40cp-1 (RZ)
{0x338fU, 0xb903U, 0U, 1U, 0U},
// x = 0x1.fep-3, log10f16(x) = -0x1.35p-1 (RZ)
{0x33f8U, 0xb8d4U, 0U, 1U, 1U},
#ifndef LIBC_TARGET_CPU_HAS_FMA
// x = 0x1.394p-1, log10f16(x) = -0x1.b4cp-3 (RZ)
{0x38e5U, 0xb2d3U, 0U, 1U, 1U},
#endif
// x = 0x1.ea8p-1, log10f16(x) = -0x1.31p-6 (RZ)
{0x3baaU, 0xa4c4U, 0U, 1U, 1U},
// x = 0x1.ebp-1, log10f16(x) = -0x1.29cp-6 (RZ)
{0x3bacU, 0xa4a7U, 0U, 1U, 1U},
// x = 0x1.f3p-1, log10f16(x) = -0x1.6dcp-7 (RZ)
{0x3bccU, 0xa1b7U, 0U, 1U, 1U},
// x = 0x1.f38p-1, log10f16(x) = -0x1.5f8p-7 (RZ)
#ifndef LIBC_TARGET_CPU_HAS_FMA
{0x3bceU, 0xa17eU, 0U, 1U, 1U},
// x = 0x1.fd8p-1, log10f16(x) = -0x1.168p-9 (RZ)
{0x3bf6U, 0x985aU, 0U, 1U, 1U},
// x = 0x1.ff8p-1, log10f16(x) = -0x1.bccp-12 (RZ)
{0x3bfeU, 0x8ef3U, 0U, 1U, 1U},
// x = 0x1.374p+0, log10f16(x) = 0x1.5b8p-4 (RZ)
{0x3cddU, 0x2d6eU, 1U, 0U, 1U},
// x = 0x1.3ecp+1, log10f16(x) = 0x1.958p-2 (RZ)
{0x40fbU, 0x3656U, 1U, 0U, 1U},
#endif
// x = 0x1.4p+3, log10f16(x) = 0x1p+0 (RZ)
{0x4900U, 0x3c00U, 0U, 0U, 0U},
// x = 0x1.9p+6, log10f16(x) = 0x1p+1 (RZ)
{0x5640U, 0x4000U, 0U, 0U, 0U},
// x = 0x1.f84p+6, log10f16(x) = 0x1.0ccp+1 (RZ)
{0x57e1U, 0x4033U, 1U, 0U, 0U},
// x = 0x1.f4p+9, log10f16(x) = 0x1.8p+1 (RZ)
{0x63d0U, 0x4200U, 0U, 0U, 0U},
// x = 0x1.388p+13, log10f16(x) = 0x1p+2 (RZ)
{0x70e2U, 0x4400U, 0U, 0U, 0U},
// x = 0x1.674p+13, log10f16(x) = 0x1.03cp+2 (RZ)
{0x719dU, 0x440fU, 1U, 0U, 0U},
}};

LLVM_LIBC_FUNCTION(float16, log10f16, (float16 x)) {
using FPBits = fputil::FPBits<float16>;
FPBits x_bits(x);

uint16_t x_u = x_bits.uintval();

// If x <= 0, or x is 1, or x is +inf, or x is NaN.
if (LIBC_UNLIKELY(x_u == 0U || x_u == 0x3c00U || x_u >= 0x7c00U)) {
// log10(NaN) = NaN
if (x_bits.is_nan()) {
if (x_bits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

return x;
}

// log10(+/-0) = −inf
if ((x_u & 0x7fffU) == 0U) {
fputil::raise_except_if_required(FE_DIVBYZERO);
return FPBits::inf(Sign::NEG).get_val();
}

if (x_u == 0x3c00U)
return FPBits::zero().get_val();

// When x < 0.
if (x_u > 0x8000U) {
fputil::set_errno_if_required(EDOM);
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

// log10(+inf) = +inf
return FPBits::inf().get_val();
}

if (auto r = LOG10F16_EXCEPTS.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
return r.value();

// To compute log10(x), we perform the following range reduction:
// x = 2^m * 1.mant,
// log10(x) = m * log10(2) + log10(1.mant).
// To compute log10(1.mant), let f be the highest 6 bits including the hidden
// bit, and d be the difference (1.mant - f), i.e., the remaining 5 bits of
// the mantissa, then:
// log10(1.mant) = log10(f) + log10(1.mant / f)
// = log10(f) + log10(1 + d/f)
// since d/f is sufficiently small.
// We store log10(f) and 1/f in the lookup tables LOG10F_F and ONE_OVER_F
// respectively.

int m = -FPBits::EXP_BIAS;

// When x is subnormal.
if ((x_u & FPBits::EXP_MASK) == 0U) {
// Normalize x.
x_bits = FPBits(x_bits.get_val() *
static_cast<float16>((1U << FPBits::FRACTION_LEN)));
x_u = x_bits.uintval();
m -= FPBits::FRACTION_LEN;
}

uint16_t mant = x_bits.get_mantissa();
// Leading 10 - 5 = 5 bits of the mantissa.
int f = mant >> 5;
// Unbiased exponent.
m += x_u >> FPBits::FRACTION_LEN;

// Set bits to 1.mant instead of 2^m * 1.mant.
x_bits.set_biased_exponent(FPBits::EXP_BIAS);
float mant_f = x_bits.get_val();
// v = 1.mant * 1/f - 1 = d/f
float v = fputil::multiply_add(mant_f, ONE_OVER_F[f], -1.0f);

// Degree-3 minimax polynomial generated by Sollya with the following
// commands:
// > display = hexadecimal;
// > P = fpminimax(log10(1 + x)/x, 2, [|SG...|], [-2^-5, 2^-5]);
// > x * P;
float log10p1_d_over_f =
v * fputil::polyeval(v, 0x1.bcb7bp-2f, -0x1.bce168p-3f, 0x1.28acb8p-3f);
// log10(1.mant) = log10(f) + log10(1 + d/f)
float log10_1_mant = LOG10F_F[f] + log10p1_d_over_f;
return static_cast<float16>(
fputil::multiply_add(static_cast<float>(m), LOG10F_2, log10_1_mant));
}

} // namespace LIBC_NAMESPACE_DECL
147 changes: 147 additions & 0 deletions libc/src/math/generic/log2f16.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
//===-- Implementation of log2f16 function --------------------------------===//
//
// 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/log2f16.h"
#include "expxf16.h"
#include "hdr/errno_macros.h"
#include "hdr/fenv_macros.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/except_value_utils.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h"
#include "src/__support/macros/properties/cpu_features.h"

namespace LIBC_NAMESPACE_DECL {

#ifdef LIBC_TARGET_CPU_HAS_FMA
static constexpr size_t N_LOG2F16_EXCEPTS = 2;
#else
static constexpr size_t N_LOG2F16_EXCEPTS = 9;
#endif

static constexpr fputil::ExceptValues<float16, N_LOG2F16_EXCEPTS>
LOG2F16_EXCEPTS = {{
#ifndef LIBC_TARGET_CPU_HAS_FMA
// x = 0x1.224p-1, log2f16(x) = -0x1.a34p-1 (RZ)
{0x3889U, 0xba8dU, 0U, 1U, 0U},
// x = 0x1.e34p-1, log2f16(x) = -0x1.558p-4 (RZ)
{0x3b8dU, 0xad56U, 0U, 1U, 0U},
#endif
// x = 0x1.e8cp-1, log2f16(x) = -0x1.128p-4 (RZ)
{0x3ba3U, 0xac4aU, 0U, 1U, 0U},
#ifndef LIBC_TARGET_CPU_HAS_FMA
// x = 0x1.f98p-1, log2f16(x) = -0x1.2ep-6 (RZ)
{0x3be6U, 0xa4b8U, 0U, 1U, 0U},
// x = 0x1.facp-1, log2f16(x) = -0x1.e7p-7 (RZ)
{0x3bebU, 0xa39cU, 0U, 1U, 1U},
#endif
// x = 0x1.fb4p-1, log2f16(x) = -0x1.b88p-7 (RZ)
{0x3bedU, 0xa2e2U, 0U, 1U, 1U},
#ifndef LIBC_TARGET_CPU_HAS_FMA
// x = 0x1.fecp-1, log2f16(x) = -0x1.cep-9 (RZ)
{0x3bfbU, 0x9b38U, 0U, 1U, 1U},
// x = 0x1.ffcp-1, log2f16(x) = -0x1.714p-11 (RZ)
{0x3bffU, 0x91c5U, 0U, 1U, 1U},
// x = 0x1.224p+0, log2f16(x) = 0x1.72cp-3 (RZ)
{0x3c89U, 0x31cbU, 1U, 0U, 1U},
#endif
}};

LLVM_LIBC_FUNCTION(float16, log2f16, (float16 x)) {
using FPBits = fputil::FPBits<float16>;
FPBits x_bits(x);

uint16_t x_u = x_bits.uintval();

// If x <= 0, or x is 1, or x is +inf, or x is NaN.
if (LIBC_UNLIKELY(x_u == 0U || x_u == 0x3c00U || x_u >= 0x7c00U)) {
// log2(NaN) = NaN
if (x_bits.is_nan()) {
if (x_bits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

return x;
}

// log2(+/-0) = −inf
if ((x_u & 0x7fffU) == 0U) {
fputil::raise_except_if_required(FE_DIVBYZERO);
return FPBits::inf(Sign::NEG).get_val();
}

if (x_u == 0x3c00U)
return FPBits::zero().get_val();

// When x < 0.
if (x_u > 0x8000U) {
fputil::set_errno_if_required(EDOM);
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

// log2(+inf) = +inf
return FPBits::inf().get_val();
}

if (auto r = LOG2F16_EXCEPTS.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
return r.value();

// To compute log2(x), we perform the following range reduction:
// x = 2^m * 1.mant,
// log2(x) = m + log2(1.mant).
// To compute log2(1.mant), let f be the highest 6 bits including the hidden
// bit, and d be the difference (1.mant - f), i.e., the remaining 5 bits of
// the mantissa, then:
// log2(1.mant) = log2(f) + log2(1.mant / f)
// = log2(f) + log2(1 + d/f)
// since d/f is sufficiently small.
// We store log2(f) and 1/f in the lookup tables LOG2F_F and ONE_OVER_F
// respectively.

int m = -FPBits::EXP_BIAS;

// When x is subnormal.
if ((x_u & FPBits::EXP_MASK) == 0U) {
// Normalize x.
x_bits = FPBits(x_bits.get_val() *
static_cast<float16>((1U << FPBits::FRACTION_LEN)));
x_u = x_bits.uintval();
m -= FPBits::FRACTION_LEN;
}

uint16_t mant = x_bits.get_mantissa();
// Leading 10 - 5 = 5 bits of the mantissa.
int f = mant >> 5;
// Unbiased exponent.
m += x_u >> FPBits::FRACTION_LEN;

// Set bits to 1.mant instead of 2^m * 1.mant.
x_bits.set_biased_exponent(FPBits::EXP_BIAS);
float mant_f = x_bits.get_val();
// v = 1.mant * 1/f - 1 = d/f
float v = fputil::multiply_add(mant_f, ONE_OVER_F[f], -1.0f);

// Degree-3 minimax polynomial generated by Sollya with the following
// commands:
// > display = hexadecimal;
// > P = fpminimax(log2(1 + x)/x, 2, [|SG...|], [-2^-5, 2^-5]);
// > x * P;
float log2p1_d_over_f =
v * fputil::polyeval(v, 0x1.715476p+0f, -0x1.71771ap-1f, 0x1.ecb38ep-2f);
// log2(1.mant) = log2(f) + log2(1 + d/f)
float log2_1_mant = LOG2F_F[f] + log2p1_d_over_f;
return static_cast<float16>(static_cast<float>(m) + log2_1_mant);
}

} // namespace LIBC_NAMESPACE_DECL
158 changes: 158 additions & 0 deletions libc/src/math/generic/logf16.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
//===-- Half-precision log(x) function ------------------------------------===//
//
// 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/logf16.h"
#include "expxf16.h"
#include "hdr/errno_macros.h"
#include "hdr/fenv_macros.h"
#include "src/__support/CPP/array.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/except_value_utils.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h"
#include "src/__support/macros/properties/cpu_features.h"

namespace LIBC_NAMESPACE_DECL {

#ifdef LIBC_TARGET_CPU_HAS_FMA
static constexpr size_t N_LOGF16_EXCEPTS = 5;
#else
static constexpr size_t N_LOGF16_EXCEPTS = 11;
#endif

static constexpr fputil::ExceptValues<float16, N_LOGF16_EXCEPTS>
LOGF16_EXCEPTS = {{
// (input, RZ output, RU offset, RD offset, RN offset)
#ifndef LIBC_TARGET_CPU_HAS_FMA
// x = 0x1.61cp-13, logf16(x) = -0x1.16p+3 (RZ)
{0x0987U, 0xc858U, 0U, 1U, 0U},
// x = 0x1.f2p-12, logf16(x) = -0x1.e98p+2 (RZ)
{0x0fc8U, 0xc7a6U, 0U, 1U, 1U},
// x = 0x1.4d4p-9, logf16(x) = -0x1.7e4p+2 (RZ)
#endif
// x = 0x1.4d4p-9, logf16(x) = -0x1.7e4p+2 (RZ)
{0x1935U, 0xc5f9U, 0U, 1U, 0U},
// x = 0x1.5ep-8, logf16(x) = -0x1.4ecp+2 (RZ)
{0x1d78U, 0xc53bU, 0U, 1U, 0U},
#ifndef LIBC_TARGET_CPU_HAS_FMA
// x = 0x1.fdp-1, logf16(x) = -0x1.81p-8 (RZ)
{0x3bf4U, 0x9e04U, 0U, 1U, 1U},
// x = 0x1.fep-1, logf16(x) = -0x1.008p-8 (RZ)
{0x3bf8U, 0x9c02U, 0U, 1U, 0U},
#endif
// x = 0x1.ffp-1, logf16(x) = -0x1.004p-9 (RZ)
{0x3bfcU, 0x9801U, 0U, 1U, 0U},
// x = 0x1.ff8p-1, logf16(x) = -0x1p-10 (RZ)
{0x3bfeU, 0x9400U, 0U, 1U, 1U},
#ifdef LIBC_TARGET_CPU_HAS_FMA
// x = 0x1.4c4p+1, logf16(x) = 0x1.e84p-1 (RZ)
{0x4131U, 0x3ba1U, 1U, 0U, 1U},
#else
// x = 0x1.75p+2, logf16(x) = 0x1.c34p+0 (RZ)
{0x45d4U, 0x3f0dU, 1U, 0U, 0U},
// x = 0x1.75p+2, logf16(x) = 0x1.c34p+0 (RZ)
{0x45d4U, 0x3f0dU, 1U, 0U, 0U},
// x = 0x1.d5p+9, logf16(x) = 0x1.b5cp+2 (RZ)
{0x6354U, 0x46d7U, 1U, 0U, 1U},
#endif
}};

LLVM_LIBC_FUNCTION(float16, logf16, (float16 x)) {
using FPBits = fputil::FPBits<float16>;
FPBits x_bits(x);

uint16_t x_u = x_bits.uintval();

// If x <= 0, or x is 1, or x is +inf, or x is NaN.
if (LIBC_UNLIKELY(x_u == 0U || x_u == 0x3c00U || x_u >= 0x7c00U)) {
// log(NaN) = NaN
if (x_bits.is_nan()) {
if (x_bits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

return x;
}

// log(+/-0) = −inf
if ((x_u & 0x7fffU) == 0U) {
fputil::raise_except_if_required(FE_DIVBYZERO);
return FPBits::inf(Sign::NEG).get_val();
}

if (x_u == 0x3c00U)
return FPBits::zero().get_val();

// When x < 0.
if (x_u > 0x8000) {
fputil::set_errno_if_required(EDOM);
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

// log(+inf) = +inf
return FPBits::inf().get_val();
}

if (auto r = LOGF16_EXCEPTS.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
return r.value();

// To compute log(x), we perform the following range reduction:
// x = 2^m * 1.mant,
// log(x) = m * log(2) + log(1.mant).
// To compute log(1.mant), let f be the highest 6 bits including the hidden
// bit, and d be the difference (1.mant - f), i.e., the remaining 5 bits of
// the mantissa, then:
// log(1.mant) = log(f) + log(1.mant / f)
// = log(f) + log(1 + d/f)
// since d/f is sufficiently small.
// We store log(f) and 1/f in the lookup tables LOGF_F and ONE_OVER_F
// respectively.

int m = -FPBits::EXP_BIAS;

// When x is subnormal.
if ((x_u & FPBits::EXP_MASK) == 0U) {
// Normalize x.
x_bits = FPBits(x_bits.get_val() *
static_cast<float16>((1U << FPBits::FRACTION_LEN)));
x_u = x_bits.uintval();
m -= FPBits::FRACTION_LEN;
}

uint16_t mant = x_bits.get_mantissa();
// Leading 10 - 5 = 5 bits of the mantissa.
int f = mant >> 5;
// Unbiased exponent.
m += x_u >> FPBits::FRACTION_LEN;

// Set bits to 1.mant instead of 2^m * 1.mant.
x_bits.set_biased_exponent(FPBits::EXP_BIAS);
float mant_f = x_bits.get_val();
// v = 1.mant * 1/f - 1 = d/f
float v = fputil::multiply_add(mant_f, ONE_OVER_F[f], -1.0f);

// Degree-3 minimax polynomial generated by Sollya with the following
// commands:
// > display = hexadecimal;
// > P = fpminimax(log(1 + x)/x, 2, [|SG...|], [-2^-5, 2^-5]);
// > x * P;
float log1p_d_over_f =
v * fputil::polyeval(v, 0x1p+0f, -0x1.001804p-1f, 0x1.557ef6p-2f);
// log(1.mant) = log(f) + log(1 + d/f)
float log_1_mant = LOGF_F[f] + log1p_d_over_f;
return static_cast<float16>(
fputil::multiply_add(static_cast<float>(m), LOGF_2, log_1_mant));
}

} // namespace LIBC_NAMESPACE_DECL
144 changes: 144 additions & 0 deletions libc/src/math/generic/sinhf16.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
//===-- Half-precision sinh(x) function -----------------------------------===//
//
// 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/sinhf16.h"
#include "expxf16.h"
#include "hdr/errno_macros.h"
#include "hdr/fenv_macros.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/except_value_utils.h"
#include "src/__support/FPUtil/rounding_mode.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h"

namespace LIBC_NAMESPACE_DECL {

static constexpr fputil::ExceptValues<float16, 16> SINHF16_EXCEPTS_POS = {{
// x = 0x1.714p-5, sinhf16(x) = 0x1.714p-5 (RZ)
{0x29c5U, 0x29c5U, 1U, 0U, 1U},
// x = 0x1.25p-4, sinhf16(x) = 0x1.25p-4 (RZ)
{0x2c94U, 0x2c94U, 1U, 0U, 1U},
// x = 0x1.f5p-4, sinhf16(x) = 0x1.f64p-4 (RZ)
{0x2fd4U, 0x2fd9U, 1U, 0U, 0U},
// x = 0x1.b1cp-3, sinhf16(x) = 0x1.b4cp-3 (RZ)
{0x32c7U, 0x32d3U, 1U, 0U, 1U},
// x = 0x1.6e8p-2, sinhf16(x) = 0x1.764p-2 (RZ)
{0x35baU, 0x35d9U, 1U, 0U, 1U},
// x = 0x1.6b4p-1, sinhf16(x) = 0x1.8a4p-1 (RZ)
{0x39adU, 0x3a29U, 1U, 0U, 1U},
// x = 0x1.a58p-1, sinhf16(x) = 0x1.d68p-1 (RZ)
{0x3a96U, 0x3b5aU, 1U, 0U, 1U},
// x = 0x1.574p+0, sinhf16(x) = 0x1.c78p+0 (RZ)
{0x3d5dU, 0x3f1eU, 1U, 0U, 1U},
// x = 0x1.648p+1, sinhf16(x) = 0x1.024p+3 (RZ)
{0x4192U, 0x4809U, 1U, 0U, 0U},
// x = 0x1.cdcp+1, sinhf16(x) = 0x1.26cp+4 (RZ)
{0x4337U, 0x4c9bU, 1U, 0U, 0U},
// x = 0x1.d0cp+1, sinhf16(x) = 0x1.2d8p+4 (RZ)
{0x4343U, 0x4cb6U, 1U, 0U, 1U},
// x = 0x1.018p+2, sinhf16(x) = 0x1.bfp+4 (RZ)
{0x4406U, 0x4efcU, 1U, 0U, 0U},
// x = 0x1.2fcp+2, sinhf16(x) = 0x1.cc4p+5 (RZ)
{0x44bfU, 0x5331U, 1U, 0U, 1U},
// x = 0x1.4ecp+2, sinhf16(x) = 0x1.75cp+6 (RZ)
{0x453bU, 0x55d7U, 1U, 0U, 0U},
// x = 0x1.8a4p+2, sinhf16(x) = 0x1.d94p+7 (RZ)
{0x4629U, 0x5b65U, 1U, 0U, 1U},
// x = 0x1.5fp+3, sinhf16(x) = 0x1.c54p+14 (RZ)
{0x497cU, 0x7715U, 1U, 0U, 1U},
}};

static constexpr fputil::ExceptValues<float16, 12> SINHF16_EXCEPTS_NEG = {{
// x = -0x1.714p-5, sinhf16(x) = -0x1.714p-5 (RZ)
{0xa9c5U, 0xa9c5U, 0U, 1U, 1U},
// x = -0x1.25p-4, sinhf16(x) = -0x1.25p-4 (RZ)
{0xac94U, 0xac94U, 0U, 1U, 1U},
// x = -0x1.f5p-4, sinhf16(x) = -0x1.f64p-4 (RZ)
{0xafd4U, 0xafd9U, 0U, 1U, 0U},
// x = -0x1.6e8p-2, sinhf16(x) = -0x1.764p-2 (RZ)
{0xb5baU, 0xb5d9U, 0U, 1U, 1U},
// x = -0x1.a58p-1, sinhf16(x) = -0x1.d68p-1 (RZ)
{0xba96U, 0xbb5aU, 0U, 1U, 1U},
// x = -0x1.cdcp+1, sinhf16(x) = -0x1.26cp+4 (RZ)
{0xc337U, 0xcc9bU, 0U, 1U, 0U},
// x = -0x1.d0cp+1, sinhf16(x) = -0x1.2d8p+4 (RZ)
{0xc343U, 0xccb6U, 0U, 1U, 1U},
// x = -0x1.018p+2, sinhf16(x) = -0x1.bfp+4 (RZ)
{0xc406U, 0xcefcU, 0U, 1U, 0U},
// x = -0x1.2fcp+2, sinhf16(x) = -0x1.cc4p+5 (RZ)
{0xc4bfU, 0xd331U, 0U, 1U, 1U},
// x = -0x1.4ecp+2, sinhf16(x) = -0x1.75cp+6 (RZ)
{0xc53bU, 0xd5d7U, 0U, 1U, 0U},
// x = -0x1.8a4p+2, sinhf16(x) = -0x1.d94p+7 (RZ)
{0xc629U, 0xdb65U, 0U, 1U, 1U},
// x = -0x1.5fp+3, sinhf16(x) = -0x1.c54p+14 (RZ)
{0xc97cU, 0xf715U, 0U, 1U, 1U},
}};

LLVM_LIBC_FUNCTION(float16, sinhf16, (float16 x)) {
using FPBits = fputil::FPBits<float16>;
FPBits x_bits(x);

uint16_t x_u = x_bits.uintval();
uint16_t x_abs = x_u & 0x7fffU;

// When |x| = 0, or -2^(-14) <= x <= -2^(-9), or |x| >= asinh(2^16), or x is
// NaN.
if (LIBC_UNLIKELY(x_abs == 0U || (x_u >= 0x8400U && x_u <= 0xa400U) ||
x_abs >= 0x49e5U)) {
// sinh(NaN) = NaN
if (x_bits.is_nan()) {
if (x_bits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

return x;
}

// sinh(+/-0) = sinh(+/-0)
if (x_abs == 0U)
return FPBits::zero(x_bits.sign()).get_val();

// When |x| >= asinh(2^16).
if (x_abs >= 0x49e5U) {
// sinh(+/-inf) = +/-inf
if (x_bits.is_inf())
return FPBits::inf(x_bits.sign()).get_val();

int rounding_mode = fputil::quick_get_round();
if (rounding_mode == FE_TONEAREST ||
(x_bits.is_pos() && rounding_mode == FE_UPWARD) ||
(x_bits.is_neg() && rounding_mode == FE_DOWNWARD)) {
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
return FPBits::inf(x_bits.sign()).get_val();
}
return FPBits::max_normal(x_bits.sign()).get_val();
}

// When -2^(-14) <= x <= -2^(-9).
if (fputil::fenv_is_round_down())
return FPBits(static_cast<uint16_t>(x_u + 1)).get_val();
return FPBits(static_cast<uint16_t>(x_u)).get_val();
}

if (x_bits.is_pos()) {
if (auto r = SINHF16_EXCEPTS_POS.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
return r.value();
} else {
if (auto r = SINHF16_EXCEPTS_NEG.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
return r.value();
}

return eval_sinh_or_cosh</*IsSinh=*/true>(x);
}

} // namespace LIBC_NAMESPACE_DECL
20 changes: 20 additions & 0 deletions libc/src/math/generic/sqrtf16.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation of sqrtf16 function --------------------------------===//
//
// 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/sqrtf16.h"
#include "src/__support/FPUtil/sqrt.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"

namespace LIBC_NAMESPACE_DECL {

LLVM_LIBC_FUNCTION(float16, sqrtf16, (float16 x)) {
return fputil::sqrt<float16>(x);
}

} // namespace LIBC_NAMESPACE_DECL
144 changes: 144 additions & 0 deletions libc/src/math/generic/tanhf16.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
//===-- Half-precision tanh(x) function -----------------------------------===//
//
// 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/tanhf16.h"
#include "expxf16.h"
#include "hdr/fenv_macros.h"
#include "src/__support/CPP/array.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/except_value_utils.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/FPUtil/nearest_integer.h"
#include "src/__support/FPUtil/rounding_mode.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h"

namespace LIBC_NAMESPACE_DECL {

static constexpr fputil::ExceptValues<float16, 2> TANHF16_EXCEPTS = {{
// x = 0x1.f54p+0, tanhf16(x) = 0x1.ecp-1 (RZ)
{0x3fd5U, 0x3bb0U, 1U, 0U, 0U},
// x = -0x1.f54p+0, tanhf16(x) = -0x1.ecp-1 (RZ)
{0xbfd5U, 0xbbb0U, 0U, 1U, 0U},
}};

LLVM_LIBC_FUNCTION(float16, tanhf16, (float16 x)) {
using FPBits = fputil::FPBits<float16>;
FPBits x_bits(x);

uint16_t x_u = x_bits.uintval();
uint16_t x_abs = x_u & 0x7fffU;

// When -2^(-14) <= x <= -2^(-9), or |x| <= 0x1.d2p-4,
// or |x| >= atanh(1 - 2^(-11)), or x is NaN.
if (LIBC_UNLIKELY((x_u >= 0x8400U && x_u <= 0x9800U) || x_abs <= 0x2f48U ||
x_abs >= 0x4429U)) {
// tanh(NaN) = NaN
if (x_bits.is_nan()) {
if (x_bits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

return x;
}

// When -2^(-14) <= x <= -2^(-9).
if (x_u >= 0x8400U && x_u <= 0x9800U) {
switch (fputil::quick_get_round()) {
case FE_TONEAREST:
case FE_DOWNWARD:
return x;
default:
return FPBits(static_cast<uint16_t>(x_u - 1U)).get_val();
}
}

// When |x| <= 0x1.d2p-4.
if (x_abs <= 0x2f48U) {
float xf = x;
float xf_sq = xf * xf;
// Degree-7 Taylor expansion generated by Sollya with the following
// commands:
// > taylor(tanh(x), 7, 0);
// > display = hexadecimal;
// > // For each coefficient:
// > round(/* put coefficient here */, SG, RN);
return static_cast<float16>(
xf * fputil::polyeval(xf_sq, 0x1p+0f, -0x1.555556p-2f, 0x1.111112p-3f,
-0x1.ba1ba2p-5f));
}

// tanh(+/-inf) = +/-1
if (x_bits.is_inf())
return FPBits::one(x_bits.sign()).get_val();

// When |x| >= atanh(1 - 2^(-11)).
fputil::raise_except_if_required(FE_INEXACT);

int rounding_mode = fputil::quick_get_round();
if ((rounding_mode == FE_TONEAREST && x_abs >= 0x4482U) ||
(rounding_mode == FE_UPWARD && x_bits.is_pos()) ||
(rounding_mode == FE_DOWNWARD && x_bits.is_neg())) {
return FPBits::one(x_bits.sign()).get_val();
}
if (x_bits.is_pos())
return static_cast<float16>(0x1.ffcp-1);
return static_cast<float16>(-0x1.ffcp-1);
}

if (auto r = TANHF16_EXCEPTS.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
return r.value();

// For atanh(-1 + 2^(-11)) < x < atanh(1 - 2^(-11)), to compute tanh(x), we
// perform the following range reduction: find hi, mid, lo, such that:
// x = (hi + mid) * log(2) * 0.5 + lo, in which
// hi is an integer,
// mid * 2^5 is an integer,
// -2^(-5) <= lo < 2^(-5).
// In particular,
// hi + mid = round(x * log2(e) * 2 * 2^5) * 2^(-5).
// Then,
// tanh(x) = sinh(x)/cosh(x)
// = (e^x - e^(-x)) / (e^x + e^(-x))
// = (e^(2x) - 1) / (e^(2x) + 1)
// = (2^(hi + mid) * e^(2*lo) - 1) / (2^(hi + mid) * e^(2*lo) + 1)
// = (e^(2*lo) - 2^(-hi - mid)) / (e^(2*lo) + 2^(-hi - mid))
// We store 2^(-mid) in the lookup table EXP2_MID_5_BITS, and compute
// 2^(-hi - mid) by adding -hi to the exponent field of 2^(-mid).
// e^lo is computed using a degree-3 minimax polynomial generated by Sollya.

float xf = x;
float kf = fputil::nearest_integer(xf * (LOG2F_E * 2.0f * 0x1.0p+5f));
int x_hi_mid = -static_cast<int>(kf);
int x_hi = x_hi_mid >> 5;
int x_mid = x_hi_mid & 0x1f;
// lo = x - (hi + mid)
// = round(x * log2(e) * 2 * 2^5) * log(2) * 0.5 * (-2^(-5)) + x
float lo = fputil::multiply_add(kf, LOGF_2 * 0.5f * -0x1.0p-5f, xf);

uint32_t exp2_hi_mid_bits =
EXP2_MID_5_BITS[x_mid] +
static_cast<uint32_t>(x_hi << fputil::FPBits<float>::FRACTION_LEN);
// exp2_hi_mid = 2^(-hi - mid)
float exp2_hi_mid = fputil::FPBits<float>(exp2_hi_mid_bits).get_val();
// Degree-3 minimax polynomial generated by Sollya with the following
// commands:
// > display = hexadecimal;
// > P = fpminimax(expm1(2*x)/x, 2, [|SG...|], [-2^-5, 2^-5]);
// > 1 + x * P;
float exp_2lo =
fputil::polyeval(lo, 0x1p+0f, 0x1p+1f, 0x1.001p+1f, 0x1.555ddep+0f);
return static_cast<float16>((exp_2lo - exp2_hi_mid) /
(exp_2lo + exp2_hi_mid));
}

} // namespace LIBC_NAMESPACE_DECL
21 changes: 21 additions & 0 deletions libc/src/math/log10f16.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- Implementation header for log10f16 ----------------------*- 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_LOG10F16_H
#define LLVM_LIBC_SRC_MATH_LOG10F16_H

#include "src/__support/macros/config.h"
#include "src/__support/macros/properties/types.h"

namespace LIBC_NAMESPACE_DECL {

float16 log10f16(float16 x);

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC_MATH_LOG10F16_H
21 changes: 21 additions & 0 deletions libc/src/math/log2f16.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- Implementation header for log2f16 -----------------------*- 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_LOG2F16_H
#define LLVM_LIBC_SRC_MATH_LOG2F16_H

#include "src/__support/macros/config.h"
#include "src/__support/macros/properties/types.h"

namespace LIBC_NAMESPACE_DECL {

float16 log2f16(float16 x);

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC_MATH_LOG2F16_H
21 changes: 21 additions & 0 deletions libc/src/math/logf16.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- Implementation header for logf16 ------------------------*- 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_LOGF16_H
#define LLVM_LIBC_SRC_MATH_LOGF16_H

#include "src/__support/macros/config.h"
#include "src/__support/macros/properties/types.h"

namespace LIBC_NAMESPACE_DECL {

float16 logf16(float16 x);

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC_MATH_LOGF16_H
21 changes: 21 additions & 0 deletions libc/src/math/sinhf16.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- Implementation header for sinhf16 -----------------------*- 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_SINHF16_H
#define LLVM_LIBC_SRC_MATH_SINHF16_H

#include "src/__support/macros/config.h"
#include "src/__support/macros/properties/types.h"

namespace LIBC_NAMESPACE_DECL {

float16 sinhf16(float16 x);

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC_MATH_SINHF16_H
21 changes: 21 additions & 0 deletions libc/src/math/sqrtf16.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- Implementation header for sqrtf16 -----------------------*- 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_SQRTF16_H
#define LLVM_LIBC_SRC_MATH_SQRTF16_H

#include "src/__support/macros/config.h"
#include "src/__support/macros/properties/types.h"

namespace LIBC_NAMESPACE_DECL {

float16 sqrtf16(float16 x);

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC_MATH_SQRTF16_H
21 changes: 21 additions & 0 deletions libc/src/math/tanhf16.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- Implementation header for tanhf16 -----------------------*- 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_TANHF16_H
#define LLVM_LIBC_SRC_MATH_TANHF16_H

#include "src/__support/macros/config.h"
#include "src/__support/macros/properties/types.h"

namespace LIBC_NAMESPACE_DECL {

float16 tanhf16(float16 x);

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC_MATH_TANHF16_H
13 changes: 13 additions & 0 deletions libc/test/UnitTest/FPMatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,4 +266,17 @@ template <typename T> struct FPTest : public Test {
EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \
(expected), (actual), (expected_except), RoundingMode::TowardZero)

#define EXPECT_FP_EQ_WITH_EXCEPTION_ALL_ROUNDING(expected, actual, \
expected_except) \
do { \
EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_NEAREST((expected), (actual), \
(expected_except)); \
EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_UPWARD((expected), (actual), \
(expected_except)); \
EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_DOWNWARD((expected), (actual), \
(expected_except)); \
EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_TOWARD_ZERO((expected), (actual), \
(expected_except)); \
} while (0)

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

add_fp_unittest(
exp2m1f16_test
NEED_MPFR
SUITE
libc-math-unittests
SRCS
exp2m1f16_test.cpp
DEPENDS
libc.src.math.exp2m1f16
)

add_fp_unittest(
exp10_test
NEED_MPFR
Expand Down Expand Up @@ -1040,6 +1051,17 @@ add_fp_unittest(
libc.src.math.exp10f16
)

add_fp_unittest(
exp10m1f16_test
NEED_MPFR
SUITE
libc-math-unittests
SRCS
exp10m1f16_test.cpp
DEPENDS
libc.src.math.exp10m1f16
)

add_fp_unittest(
copysign_test
SUITE
Expand Down Expand Up @@ -1457,6 +1479,17 @@ add_fp_unittest(
libc.src.math.sqrtl
)

add_fp_unittest(
sqrtf16_test
NEED_MPFR
SUITE
libc-math-unittests
SRCS
sqrtf16_test.cpp
DEPENDS
libc.src.math.sqrtf16
)

add_fp_unittest(
generic_sqrtf_test
NEED_MPFR
Expand Down Expand Up @@ -1739,6 +1772,17 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
logf16_test
NEED_MPFR
SUITE
libc-math-unittests
SRCS
logf16_test.cpp
DEPENDS
libc.src.math.logf16
)

add_fp_unittest(
log2_test
NEED_MPFR
Expand All @@ -1765,6 +1809,17 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
log2f16_test
NEED_MPFR
SUITE
libc-math-unittests
SRCS
log2f16_test.cpp
DEPENDS
libc.src.math.log2f16
)

add_fp_unittest(
log10_test
NEED_MPFR
Expand All @@ -1791,6 +1846,17 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
log10f16_test
NEED_MPFR
SUITE
libc-math-unittests
SRCS
log10f16_test.cpp
DEPENDS
libc.src.math.log10f16
)

add_fp_unittest(
log1p_test
NEED_MPFR
Expand Down Expand Up @@ -1883,6 +1949,17 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
coshf16_test
NEED_MPFR
SUITE
libc-math-unittests
SRCS
coshf16_test.cpp
DEPENDS
libc.src.math.coshf16
)

add_fp_unittest(
sinhf_test
NEED_MPFR
Expand All @@ -1899,6 +1976,17 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
sinhf16_test
NEED_MPFR
SUITE
libc-math-unittests
SRCS
sinhf16_test.cpp
DEPENDS
libc.src.math.sinhf16
)

add_fp_unittest(
tanhf_test
NEED_MPFR
Expand All @@ -1911,6 +1999,17 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
tanhf16_test
NEED_MPFR
SUITE
libc-math-unittests
SRCS
tanhf16_test.cpp
DEPENDS
libc.src.math.tanhf16
)

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

using LlvmLibcCoshf16Test = 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(LlvmLibcCoshf16Test, PositiveRange) {
for (uint16_t v = POS_START; v <= POS_STOP; ++v) {
float16 x = FPBits(v).get_val();
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cosh, x,
LIBC_NAMESPACE::coshf16(x), 0.5);
}
}

TEST_F(LlvmLibcCoshf16Test, NegativeRange) {
for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) {
float16 x = FPBits(v).get_val();
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cosh, x,
LIBC_NAMESPACE::coshf16(x), 0.5);
}
}
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);
}
}
40 changes: 40 additions & 0 deletions libc/test/src/math/exp2m1f16_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//===-- Exhaustive test for exp2m1f16 -------------------------------------===//
//
// 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/exp2m1f16.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
#include "utils/MPFRWrapper/MPFRUtils.h"

using LlvmLibcExp2m1f16Test = 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(LlvmLibcExp2m1f16Test, PositiveRange) {
for (uint16_t v = POS_START; v <= POS_STOP; ++v) {
float16 x = FPBits(v).get_val();
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp2m1, x,
LIBC_NAMESPACE::exp2m1f16(x), 0.5);
}
}

TEST_F(LlvmLibcExp2m1f16Test, NegativeRange) {
for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) {
float16 x = FPBits(v).get_val();
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp2m1, x,
LIBC_NAMESPACE::exp2m1f16(x), 0.5);
}
}
40 changes: 40 additions & 0 deletions libc/test/src/math/log10f16_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//===-- Exhaustive test for log10f16 --------------------------------------===//
//
// 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/log10f16.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
#include "utils/MPFRWrapper/MPFRUtils.h"

using LlvmLibcLog10f16Test = 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(LlvmLibcLog10f16Test, PositiveRange) {
for (uint16_t v = POS_START; v <= POS_STOP; ++v) {
float16 x = FPBits(v).get_val();
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Log10, x,
LIBC_NAMESPACE::log10f16(x), 0.5);
}
}

TEST_F(LlvmLibcLog10f16Test, NegativeRange) {
for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) {
float16 x = FPBits(v).get_val();
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Log10, x,
LIBC_NAMESPACE::log10f16(x), 0.5);
}
}
40 changes: 40 additions & 0 deletions libc/test/src/math/log2f16_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//===-- Exhaustive test for log2f16 ---------------------------------------===//
//
// 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/log2f16.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
#include "utils/MPFRWrapper/MPFRUtils.h"

using LlvmLibcLog2f16Test = 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(LlvmLibcLog2f16Test, PositiveRange) {
for (uint16_t v = POS_START; v <= POS_STOP; ++v) {
float16 x = FPBits(v).get_val();
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Log2, x,
LIBC_NAMESPACE::log2f16(x), 0.5);
}
}

TEST_F(LlvmLibcLog2f16Test, NegativeRange) {
for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) {
float16 x = FPBits(v).get_val();
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Log2, x,
LIBC_NAMESPACE::log2f16(x), 0.5);
}
}
40 changes: 40 additions & 0 deletions libc/test/src/math/logf16_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//===-- Exhaustive test for logf16 ---------------------------------------===//
//
// 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/logf16.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
#include "utils/MPFRWrapper/MPFRUtils.h"

using LlvmLibcLogf16Test = 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(LlvmLibcLogf16Test, PositiveRange) {
for (uint16_t v = POS_START; v <= POS_STOP; ++v) {
float16 x = FPBits(v).get_val();
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Log, x,
LIBC_NAMESPACE::logf16(x), 0.5);
}
}

TEST_F(LlvmLibcLogf16Test, NegativeRange) {
for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) {
float16 x = FPBits(v).get_val();
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Log, x,
LIBC_NAMESPACE::logf16(x), 0.5);
}
}
40 changes: 40 additions & 0 deletions libc/test/src/math/sinhf16_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//===-- Exhaustive test for sinhf16 ---------------------------------------===//
//
// 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/sinhf16.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
#include "utils/MPFRWrapper/MPFRUtils.h"

using LlvmLibcSinhf16Test = 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(LlvmLibcSinhf16Test, PositiveRange) {
for (uint16_t v = POS_START; v <= POS_STOP; ++v) {
float16 x = FPBits(v).get_val();
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sinh, x,
LIBC_NAMESPACE::sinhf16(x), 0.5);
}
}

TEST_F(LlvmLibcSinhf16Test, NegativeRange) {
for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) {
float16 x = FPBits(v).get_val();
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sinh, x,
LIBC_NAMESPACE::sinhf16(x), 0.5);
}
}
107 changes: 107 additions & 0 deletions libc/test/src/math/smoke/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,17 @@ add_fp_unittest(
libc.src.math.exp2m1f
)

add_fp_unittest(
exp2m1f16_test
SUITE
libc-math-smoke-tests
SRCS
exp2m1f16_test.cpp
DEPENDS
libc.src.errno.errno
libc.src.math.exp2m1f16
)

add_fp_unittest(
exp10_test
SUITE
Expand Down Expand Up @@ -1142,6 +1153,18 @@ add_fp_unittest(
libc.src.math.exp10f16
)

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
)

add_fp_unittest(
copysign_test
SUITE
Expand Down Expand Up @@ -2723,6 +2746,18 @@ add_fp_unittest(
libc.src.math.sqrtl
)

add_fp_unittest(
sqrtf16_test
SUITE
libc-math-smoke-tests
SRCS
sqrtf16_test.cpp
HDRS
SqrtTest.h
DEPENDS
libc.src.math.sqrtf16
)

add_fp_unittest(
sqrtf128_test
SUITE
Expand Down Expand Up @@ -3385,6 +3420,18 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
logf16_test
SUITE
libc-math-smoke-tests
SRCS
logf16_test.cpp
DEPENDS
libc.hdr.fenv_macros
libc.src.errno.errno
libc.src.math.logf16
)

add_fp_unittest(
log2_test
SUITE
Expand All @@ -3409,6 +3456,18 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
log2f16_test
SUITE
libc-math-smoke-tests
SRCS
log2f16_test.cpp
DEPENDS
libc.hdr.fenv_macros
libc.src.errno.errno
libc.src.math.log2f16
)

add_fp_unittest(
log10_test
SUITE
Expand All @@ -3433,6 +3492,18 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
log10f16_test
SUITE
libc-math-smoke-tests
SRCS
log10f16_test.cpp
DEPENDS
libc.hdr.fenv_macros
libc.src.errno.errno
libc.src.math.log10f16
)

add_fp_unittest(
log1p_test
SUITE
Expand Down Expand Up @@ -3555,6 +3626,18 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
coshf16_test
SUITE
libc-math-smoke-tests
SRCS
coshf16_test.cpp
DEPENDS
libc.hdr.fenv_macros
libc.src.errno.errno
libc.src.math.coshf16
)

add_fp_unittest(
sinhf_test
SUITE
Expand All @@ -3568,6 +3651,18 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
sinhf16_test
SUITE
libc-math-smoke-tests
SRCS
sinhf16_test.cpp
DEPENDS
libc.hdr.fenv_macros
libc.src.errno.errno
libc.src.math.sinhf16
)

add_fp_unittest(
tanhf_test
SUITE
Expand All @@ -3579,6 +3674,18 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
tanhf16_test
SUITE
libc-math-smoke-tests
SRCS
tanhf16_test.cpp
DEPENDS
libc.hdr.fenv_macros
libc.src.errno.errno
libc.src.math.tanhf16
)

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

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

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

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

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

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

EXPECT_FP_EQ_ALL_ROUNDING(inf, LIBC_NAMESPACE::coshf16(neg_inf));
EXPECT_MATH_ERRNO(0);

EXPECT_FP_EQ_ALL_ROUNDING(static_cast<float16>(1.0),
LIBC_NAMESPACE::coshf16(zero));
EXPECT_MATH_ERRNO(0);

EXPECT_FP_EQ_ALL_ROUNDING(static_cast<float16>(1.0),
LIBC_NAMESPACE::coshf16(neg_zero));
EXPECT_MATH_ERRNO(0);
}

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

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

EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::coshf16(neg_max_normal),
FE_OVERFLOW | FE_INEXACT);
EXPECT_MATH_ERRNO(ERANGE);

// round(acosh(2^16), HP, RU);
float16 x = static_cast<float16>(0x1.794p+3);

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

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

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

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

// round(-acosh(2^16), HP, RD);
x = static_cast<float16>(-0x1.794p+3);

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

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

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

EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_TOWARD_ZERO(
max_normal, LIBC_NAMESPACE::coshf16(x), FE_INEXACT);
EXPECT_MATH_ERRNO(0);
}
109 changes: 109 additions & 0 deletions libc/test/src/math/smoke/exp10m1f16_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//===-- 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/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(static_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 = static_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(static_cast<float16>(-1.0),
LIBC_NAMESPACE::exp10m1f16(neg_max_normal),
FE_INEXACT);

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

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

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

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

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

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

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

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

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

EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_TOWARD_ZERO(
static_cast<float16>(-0x1.ffcp-1), LIBC_NAMESPACE::exp10m1f16(x),
FE_INEXACT);
}
94 changes: 94 additions & 0 deletions libc/test/src/math/smoke/exp2m1f16_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//===-- Unittests for exp2m1f16 -------------------------------------------===//
//
// 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/errno_macros.h"
#include "hdr/fenv_macros.h"
#include "src/errno/libc_errno.h"
#include "src/math/exp2m1f16.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"

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

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

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

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

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

EXPECT_FP_EQ_ALL_ROUNDING(static_cast<float16>(-1.0),
LIBC_NAMESPACE::exp2m1f16(neg_inf));
EXPECT_MATH_ERRNO(0);

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

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

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

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

float16 x = static_cast<float16>(16.0);

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

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

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

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

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

EXPECT_FP_EQ_WITH_EXCEPTION(static_cast<float16>(-1.0),
LIBC_NAMESPACE::exp2m1f16(neg_max_normal),
FE_INEXACT);

EXPECT_FP_EQ_ALL_ROUNDING(
static_cast<float16>(-0x1.ffcp-1),
LIBC_NAMESPACE::exp2m1f16(static_cast<float16>(-11)));

float16 x = static_cast<float16>(-12);

EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_NEAREST(
static_cast<float16>(-1.0), LIBC_NAMESPACE::exp2m1f16(x), FE_INEXACT);

EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_UPWARD(static_cast<float16>(-0x1.ffcp-1),
LIBC_NAMESPACE::exp2m1f16(x),
FE_INEXACT);

EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_DOWNWARD(
static_cast<float16>(-1.0), LIBC_NAMESPACE::exp2m1f16(x), FE_INEXACT);

EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_TOWARD_ZERO(
static_cast<float16>(-0x1.ffcp-1), LIBC_NAMESPACE::exp2m1f16(x),
FE_INEXACT);
}
47 changes: 47 additions & 0 deletions libc/test/src/math/smoke/log10f16_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//===-- Unittests for log10f16 --------------------------------------------===//
//
// 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/errno/libc_errno.h"
#include "src/math/log10f16.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"

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

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

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

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

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

EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::log10f16(neg_inf));
EXPECT_MATH_ERRNO(EDOM);

EXPECT_FP_EQ_WITH_EXCEPTION_ALL_ROUNDING(
neg_inf, LIBC_NAMESPACE::log10f16(zero), FE_DIVBYZERO);
EXPECT_MATH_ERRNO(0);

EXPECT_FP_EQ_WITH_EXCEPTION_ALL_ROUNDING(
neg_inf, LIBC_NAMESPACE::log10f16(neg_zero), FE_DIVBYZERO);
EXPECT_MATH_ERRNO(0);

EXPECT_FP_EQ_ALL_ROUNDING(
zero, LIBC_NAMESPACE::log10f16(static_cast<float16>(1.0)));
EXPECT_MATH_ERRNO(0);

EXPECT_FP_EQ_ALL_ROUNDING(
aNaN, LIBC_NAMESPACE::log10f16(static_cast<float16>(-1.0)));
EXPECT_MATH_ERRNO(EDOM);
}
Loading