diff --git a/libc/config/baremetal/aarch64/entrypoints.txt b/libc/config/baremetal/aarch64/entrypoints.txt index 935c95af0d4af..f8d0ed5167d43 100644 --- a/libc/config/baremetal/aarch64/entrypoints.txt +++ b/libc/config/baremetal/aarch64/entrypoints.txt @@ -778,6 +778,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS libc.src.math.canonicalizebf16 libc.src.math.ceilbf16 libc.src.math.copysignbf16 + libc.src.math.expbf16 libc.src.math.fabsbf16 libc.src.math.fdimbf16 libc.src.math.floorbf16 diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt index 82e257c1d2b0d..3291d54c6e308 100644 --- a/libc/config/baremetal/arm/entrypoints.txt +++ b/libc/config/baremetal/arm/entrypoints.txt @@ -778,6 +778,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS libc.src.math.canonicalizebf16 libc.src.math.ceilbf16 libc.src.math.copysignbf16 + libc.src.math.expbf16 libc.src.math.fabsbf16 libc.src.math.fdimbf16 libc.src.math.floorbf16 diff --git a/libc/config/baremetal/riscv/entrypoints.txt b/libc/config/baremetal/riscv/entrypoints.txt index c10cc1162cc5a..bf0e7d7108bdb 100644 --- a/libc/config/baremetal/riscv/entrypoints.txt +++ b/libc/config/baremetal/riscv/entrypoints.txt @@ -778,6 +778,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS libc.src.math.canonicalizebf16 libc.src.math.ceilbf16 libc.src.math.copysignbf16 + libc.src.math.expbf16 libc.src.math.fabsbf16 libc.src.math.fdimbf16 libc.src.math.floorbf16 diff --git a/libc/config/darwin/aarch64/entrypoints.txt b/libc/config/darwin/aarch64/entrypoints.txt index e3c6c2b30c415..d5eba9cdf4f9d 100644 --- a/libc/config/darwin/aarch64/entrypoints.txt +++ b/libc/config/darwin/aarch64/entrypoints.txt @@ -608,6 +608,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS libc.src.math.canonicalizebf16 libc.src.math.ceilbf16 libc.src.math.copysignbf16 + libc.src.math.expbf16 libc.src.math.fabsbf16 libc.src.math.fdimbf16 libc.src.math.floorbf16 diff --git a/libc/config/darwin/x86_64/entrypoints.txt b/libc/config/darwin/x86_64/entrypoints.txt index e899bf97ea3f6..78dab1d24d20c 100644 --- a/libc/config/darwin/x86_64/entrypoints.txt +++ b/libc/config/darwin/x86_64/entrypoints.txt @@ -251,6 +251,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS libc.src.math.canonicalizebf16 libc.src.math.ceilbf16 libc.src.math.copysignbf16 + libc.src.math.expbf16 libc.src.math.fabsbf16 libc.src.math.fdimbf16 libc.src.math.floorbf16 diff --git a/libc/config/gpu/amdgpu/entrypoints.txt b/libc/config/gpu/amdgpu/entrypoints.txt index 0dda7d5c683ec..524e42c0f6c94 100644 --- a/libc/config/gpu/amdgpu/entrypoints.txt +++ b/libc/config/gpu/amdgpu/entrypoints.txt @@ -634,6 +634,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS libc.src.math.canonicalizebf16 libc.src.math.ceilbf16 libc.src.math.copysignbf16 + libc.src.math.expbf16 libc.src.math.fabsbf16 libc.src.math.fdimbf16 libc.src.math.floorbf16 diff --git a/libc/config/gpu/nvptx/entrypoints.txt b/libc/config/gpu/nvptx/entrypoints.txt index 6070fb5b17b3c..d6450cdba6dce 100644 --- a/libc/config/gpu/nvptx/entrypoints.txt +++ b/libc/config/gpu/nvptx/entrypoints.txt @@ -636,6 +636,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS libc.src.math.canonicalizebf16 libc.src.math.ceilbf16 libc.src.math.copysignbf16 + libc.src.math.expbf16 libc.src.math.fabsbf16 libc.src.math.fdimbf16 libc.src.math.floorbf16 diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index ae8deabc97407..184e7878dbc24 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -861,6 +861,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS libc.src.math.canonicalizebf16 libc.src.math.ceilbf16 libc.src.math.copysignbf16 + libc.src.math.expbf16 libc.src.math.fabsbf16 libc.src.math.fdimbf16 libc.src.math.floorbf16 diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt index f04ac40145d3a..999f863bbba36 100644 --- a/libc/config/linux/arm/entrypoints.txt +++ b/libc/config/linux/arm/entrypoints.txt @@ -478,6 +478,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS libc.src.math.canonicalizebf16 libc.src.math.ceilbf16 libc.src.math.copysignbf16 + libc.src.math.expbf16 libc.src.math.fabsbf16 libc.src.math.fdimbf16 libc.src.math.floorbf16 diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt index 5f407e842121e..c54ce62703131 100644 --- a/libc/config/linux/riscv/entrypoints.txt +++ b/libc/config/linux/riscv/entrypoints.txt @@ -883,6 +883,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS libc.src.math.canonicalizebf16 libc.src.math.ceilbf16 libc.src.math.copysignbf16 + libc.src.math.expbf16 libc.src.math.fabsbf16 libc.src.math.fdimbf16 libc.src.math.floorbf16 diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index bf2ad4a40ca11..e55e2664f0818 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -918,6 +918,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS libc.src.math.canonicalizebf16 libc.src.math.ceilbf16 libc.src.math.copysignbf16 + libc.src.math.expbf16 libc.src.math.fabsbf16 libc.src.math.fdimbf16 libc.src.math.floorbf16 diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt index 3a76595b258e2..750715c83cbca 100644 --- a/libc/config/windows/entrypoints.txt +++ b/libc/config/windows/entrypoints.txt @@ -324,6 +324,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS libc.src.math.canonicalizebf16 libc.src.math.ceilbf16 libc.src.math.copysignbf16 + libc.src.math.expbf16 libc.src.math.fabsbf16 libc.src.math.fdimbf16 libc.src.math.floorbf16 diff --git a/libc/docs/headers/math/index.rst b/libc/docs/headers/math/index.rst index 51bf238b950b0..c10c42408eea0 100644 --- a/libc/docs/headers/math/index.rst +++ b/libc/docs/headers/math/index.rst @@ -297,7 +297,7 @@ Higher Math Functions +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+------------------------+----------------------------+ | erfc | | | | | | | 7.12.8.2 | F.10.5.2 | +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+------------------------+----------------------------+ -| exp | |check| | |check| | | |check| | | | 7.12.6.1 | F.10.3.1 | +| exp | |check| | |check| | | |check| | | |check| | 7.12.6.1 | F.10.3.1 | +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+------------------------+----------------------------+ | exp10 | |check| | |check| | | |check| | | | 7.12.6.2 | F.10.3.2 | +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+------------------------+----------------------------+ diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt index 3c7e99f4a9c46..d74b43e0f923b 100644 --- a/libc/src/math/CMakeLists.txt +++ b/libc/src/math/CMakeLists.txt @@ -139,6 +139,7 @@ add_math_entrypoint_object(erff) add_math_entrypoint_object(exp) add_math_entrypoint_object(expf) add_math_entrypoint_object(expf16) +add_math_entrypoint_object(expbf16) add_math_entrypoint_object(exp2) add_math_entrypoint_object(exp2f) diff --git a/libc/src/math/expbf16.h b/libc/src/math/expbf16.h new file mode 100644 index 0000000000000..7c7aed30b4a51 --- /dev/null +++ b/libc/src/math/expbf16.h @@ -0,0 +1,21 @@ +//===-- Implementation header for expbf16 -----------------------*- 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_EXPBF16_H +#define LLVM_LIBC_SRC_MATH_EXPBF16_H + +#include "src/__support/macros/config.h" +#include "src/__support/macros/properties/types.h" + +namespace LIBC_NAMESPACE_DECL { + +bfloat16 expbf16(bfloat16 x); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_MATH_EXPBF16_H diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt index 8074a3925626c..0adcc7fd31015 100644 --- a/libc/src/math/generic/CMakeLists.txt +++ b/libc/src/math/generic/CMakeLists.txt @@ -1441,6 +1441,28 @@ add_entrypoint_object( libc.src.errno.errno ) +add_entrypoint_object( + expbf16 + SRCS + expbf16.cpp + HDRS + ../expbf16.h + DEPENDS + libc.hdr.errno_macros + libc.hdr.fenv_macros + libc.src.__support.CPP.array + libc.src.__support.FPUtil.cast + 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.FPUtil.bfloat16 + libc.src.__support.macros.optimization +) + add_entrypoint_object( exp2 SRCS diff --git a/libc/src/math/generic/expbf16.cpp b/libc/src/math/generic/expbf16.cpp new file mode 100644 index 0000000000000..6bb6d2e0f5288 --- /dev/null +++ b/libc/src/math/generic/expbf16.cpp @@ -0,0 +1,187 @@ +//===-- BFloat16 e^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/expbf16.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/bfloat16.h" +#include "src/__support/FPUtil/cast.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" +#include "src/__support/macros/properties/types.h" + +namespace LIBC_NAMESPACE_DECL { + +// Generated by Sollya with the following commands: +// > display = hexadecimal; +// > for i from -96 to 88 by 8 do print(i, round(exp(i), SG, RN) @ "f,"); +static constexpr float EXP_HI[24] = { + 0x1.6a4p-139f, 0x1.07b71p-127f, 0x1.7fd974p-116f, 0x1.175afp-104f, + 0x1.969d48p-93f, 0x1.27ec46p-81f, 0x1.aebabap-70f, 0x1.397924p-58f, + 0x1.c8465p-47f, 0x1.4c1078p-35f, 0x1.e355bcp-24f, 0x1.5fc21p-12f, + 0x1p0f, 0x1.749ea8p11f, 0x1.0f2ebep23f, 0x1.8ab7fcp34f, + 0x1.1f43fcp46f, 0x1.a220d4p57f, 0x1.304d6ap69f, 0x1.baed16p80f, + 0x1.425982p92f, 0x1.d531d8p103f, 0x1.55779cp115f, 0x1.f1056ep126f, +}; + +// Generated by Sollya with the following commands: +// > display = hexadecimal; +// > for i from 0 to 7.75 by 0.25 do print(round(exp(i), SG, RN) @ "f,"); +static constexpr float EXP_MID[32] = { + 0x1p0f, 0x1.48b5e4p0f, 0x1.a61298p0f, 0x1.0ef9dcp1f, + 0x1.5bf0a8p1f, 0x1.bec38ep1f, 0x1.1ed3fep2f, 0x1.704b6ap2f, + 0x1.d8e64cp2f, 0x1.2f9b88p3f, 0x1.85d6fep3f, 0x1.f4907p3f, + 0x1.415e5cp4f, 0x1.9ca53cp4f, 0x1.08ec72p5f, 0x1.542b2ep5f, + 0x1.b4c902p5f, 0x1.186bf2p6f, 0x1.68118ap6f, 0x1.ce564ep6f, + 0x1.28d38ap7f, 0x1.7d21eep7f, 0x1.e96244p7f, 0x1.3a30dp8f, + 0x1.936dc6p8f, 0x1.0301a4p9f, 0x1.4c9222p9f, 0x1.ab0786p9f, + 0x1.122886p10f, 0x1.6006b6p10f, 0x1.c402b6p10f, 0x1.223252p11f, +}; + +constexpr fputil::ExceptValues EXPBF16_EXCEPTS = {{ + // (input, RZ output, RU offset, RD offset, RN offset) + // x = 0x40DB (6.84375) + // MPFR: RU=0x446B, RD=0x446A, RZ=0x446A, RN=0x446B + {0x40DBU, 0x446AU, 1U, 0U, 1U}, + // x = 0x419D, keep original + {0x419DU, 0x4D9FU, 1U, 0U, 0U}, + // x = 0x41F9 (31.125) + // MPFR: RU=0x55F0, RD=0x55EF, RZ=0x55EF, RN=0x55F0 + {0x41F9U, 0x55EFU, 1U, 0U, 1U}, + // x = 0xC19F (-19.875) + // MPFR: RU=0x3121, RD=0x3120, RZ=0x3120, RN=0x3121 + {0xC19FU, 0x3120U, 1U, 0U, 1U}, +}}; + +LLVM_LIBC_FUNCTION(bfloat16, expbf16, (bfloat16 x)) { + using FPBits = fputil::FPBits; + FPBits x_bits(x); + + uint16_t x_u = x_bits.uintval(); + uint16_t x_abs = x_u & 0x7fffU; + + // 0 <= |x| <= 2^(-3), or |x| >= 89, or x is NaN + if (LIBC_UNLIKELY(x_abs <= 0x3e00U || x_abs >= 0x42b2U)) { + + // exp(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; + } + + // if x >= 89 + if (x_bits.is_pos() && x_abs >= 0x42b2U) { + // exp(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); + return FPBits::inf().get_val(); + default: + return FPBits::max_normal().get_val(); + } + } + + // x <= -92.5 + if (x_u >= 0xc2b9U) { + // exp(-inf) = +0 + if (x_bits.is_inf()) + return FPBits::zero().get_val(); + + fputil::set_errno_if_required(ERANGE); + fputil::raise_except_if_required(FE_UNDERFLOW | FE_INEXACT); + + switch (fputil::quick_get_round()) { + case FE_UPWARD: + case FE_TONEAREST: + if (LIBC_UNLIKELY(x_u == 0xc2b9U)) + return FPBits::min_subnormal().get_val(); + return FPBits::zero().get_val(); + default: + return FPBits::zero().get_val(); + } + } + + // 0 < |x| <= 2^(-3) + if (x_abs <= 0x3e00U && !x_bits.is_zero()) { + float xf = static_cast(x); + // Degree-3 minimax polynomial generated by Sollya with the following + // commands: + // > display = hexadecimal; + // > P = fpminimax(expm1(x)/x, 2, [|SG...|], [-2^-7, 2^-7]); + // > 1 + x * P; + // 0x1p0 + x * (0x1p0 + x * (0x1.00004p-1 + x * 0x1.555578p-3)) + return fputil::cast( + fputil::polyeval(xf, 0x1p+0f, 0x1p+0f, 0x1.0004p-1f, 0x1.555778p-3f)); + } + + // exp(0) = 1 + if (x_bits.is_zero()) + return bfloat16(1.0f); + } + + if (auto r = EXPBF16_EXCEPTS.lookup(x_u); LIBC_UNLIKELY(r.has_value())) + return r.value(); + + // For -93 < x < 89, we do the following range reduction: + // x = hi + mid + lo + // where, + // hi is an integer + // mid * 2^2 is an integer + // -2^3 <= lo <= 2^3 + // also, hi + mid = round(4 * x) / x + // then, + // exp(x) = exp(hi + mid + lo) + // = exp(hi) * exp(mid) * exp(lo) + // we store 184/8 + 1 = 24 values for looking up exp(hi) + // from -96 to 88 + // we store 8*4 = 32 values for looking up exp(mid) since + // mid will always have the bit pattern |bbb.bb| where + // b can be either 0 or 1 + + float xf = static_cast(x); + float kf = fputil::nearest_integer(xf * 4.0f); + int x_hi_mid = static_cast(kf); + int x_hi = x_hi_mid >> 5; + int x_mid = x_hi_mid & 0b11111; + // lo = x - (hi + mid) = round(x * 4) / (-4) + x + float lo = fputil::multiply_add(kf, -0.25f, xf); + + float exp_hi = EXP_HI[x_hi + 12]; + float exp_mid = EXP_MID[x_mid]; + + // Degree-3 minimax polynomial generated by Sollya with the following + // commands: + // > display = hexadecimal; + // > P = fpminimax(expm1(x)/x, 2, [|SG...|], [-2^-7, 2^-7]); + // > 1 + x * P; + // 0x1p0 + x * (0x1p0 + x * (0x1.00004p-1 + x * 0x1.555578p-3)) + float exp_lo = + fputil::polyeval(lo, 0x1p+0f, 0x1p+0f, 0x1.0004p-1f, 0x1.555778p-3f); + + return fputil::cast(exp_hi * exp_mid * exp_lo); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt index 2d2d5287bb384..299762cbbc808 100644 --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -1138,6 +1138,18 @@ add_fp_unittest( libc.src.math.expf16 ) +add_fp_unittest( + expbf16_test + NEED_MPFR + SUITE + libc-math-unittests + SRCS + expbf16_test.cpp + DEPENDS + libc.src.math.expbf16 + libc.src.__support.FPUtil.bfloat16 +) + add_fp_unittest( exp2_test NEED_MPFR diff --git a/libc/test/src/math/expbf16_test.cpp b/libc/test/src/math/expbf16_test.cpp new file mode 100644 index 0000000000000..c2288af74fe12 --- /dev/null +++ b/libc/test/src/math/expbf16_test.cpp @@ -0,0 +1,41 @@ +//===-- Exhaustive test for expbf16 ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/__support/FPUtil/bfloat16.h" +#include "src/math/expbf16.h" +#include "test/UnitTest/FPMatcher.h" +#include "test/UnitTest/Test.h" +#include "utils/MPFRWrapper/MPFRUtils.h" + +using LlvmLibcExpBf16Test = LIBC_NAMESPACE::testing::FPTest; + +namespace mpfr = LIBC_NAMESPACE::testing::mpfr; + +// range: [0, inf] +static constexpr uint16_t POS_START = 0x0000U; +static constexpr uint16_t POS_STOP = 0x7f80U; + +// range: [-0, -inf] +static constexpr uint16_t NEG_START = 0x8000U; +static constexpr uint16_t NEG_STOP = 0xff80U; + +TEST_F(LlvmLibcExpBf16Test, PositiveRange) { + for (uint16_t v = POS_START; v <= POS_STOP; ++v) { + bfloat16 x = FPBits(v).get_val(); + EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp, x, + LIBC_NAMESPACE::expbf16(x), 0.5); + } +} + +TEST_F(LlvmLibcExpBf16Test, NegativeRange) { + for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) { + bfloat16 x = FPBits(v).get_val(); + EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp, x, + LIBC_NAMESPACE::expbf16(x), 0.5); + } +} diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt index 42a97ba10b601..8373423b7bc13 100644 --- a/libc/test/src/math/smoke/CMakeLists.txt +++ b/libc/test/src/math/smoke/CMakeLists.txt @@ -1395,6 +1395,20 @@ add_fp_unittest( libc.src.__support.FPUtil.cast ) +add_fp_unittest( + expbf16_test + SUITE + libc-math-smoke-tests + SRCS + expbf16_test.cpp + DEPENDS + libc.hdr.errno_macros + libc.hdr.fenv_macros + libc.src.math.expbf16 + libc.src.__support.FPUtil.bfloat16 + libc.src.__support.FPUtil.cast +) + add_fp_unittest( exp2_test SUITE diff --git a/libc/test/src/math/smoke/expbf16_test.cpp b/libc/test/src/math/smoke/expbf16_test.cpp new file mode 100644 index 0000000000000..cc501571383e3 --- /dev/null +++ b/libc/test/src/math/smoke/expbf16_test.cpp @@ -0,0 +1,63 @@ +//===-- Unittests for expbf16 ---------------------------------------------===// +// +// 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/__support/FPUtil/bfloat16.h" +#include "src/__support/FPUtil/cast.h" +#include "src/math/expbf16.h" +#include "test/UnitTest/FPMatcher.h" +#include "test/UnitTest/Test.h" + +using LlvmLibcExpf16Test = LIBC_NAMESPACE::testing::FPTest; + +TEST_F(LlvmLibcExpf16Test, SpecialNumbers) { + EXPECT_FP_IS_NAN(LIBC_NAMESPACE::expbf16(aNaN)); + EXPECT_MATH_ERRNO(0); + + EXPECT_FP_IS_NAN_WITH_EXCEPTION(LIBC_NAMESPACE::expbf16(sNaN), FE_INVALID); + EXPECT_MATH_ERRNO(0); + + EXPECT_FP_EQ_ALL_ROUNDING(inf, LIBC_NAMESPACE::expbf16(inf)); + EXPECT_MATH_ERRNO(0); + + EXPECT_FP_EQ_ALL_ROUNDING(zero, LIBC_NAMESPACE::expbf16(neg_inf)); + EXPECT_MATH_ERRNO(0); + + EXPECT_FP_EQ_ALL_ROUNDING(LIBC_NAMESPACE::fputil::cast(1.0f), + LIBC_NAMESPACE::expbf16(zero)); + EXPECT_MATH_ERRNO(0); + + EXPECT_FP_EQ_ALL_ROUNDING(LIBC_NAMESPACE::fputil::cast(1.0f), + LIBC_NAMESPACE::expbf16(neg_zero)); + EXPECT_MATH_ERRNO(0); +} + +TEST_F(LlvmLibcExpf16Test, Overflow) { + EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::expbf16(max_normal), + FE_OVERFLOW); + EXPECT_MATH_ERRNO(ERANGE); + + EXPECT_FP_EQ_WITH_EXCEPTION( + inf, + LIBC_NAMESPACE::expbf16(LIBC_NAMESPACE::fputil::cast(89.0f)), + FE_OVERFLOW); + EXPECT_MATH_ERRNO(ERANGE); +} + +TEST_F(LlvmLibcExpf16Test, Underflow) { + EXPECT_FP_EQ_WITH_EXCEPTION(zero, LIBC_NAMESPACE::expbf16(neg_max_normal), + FE_UNDERFLOW | FE_INEXACT); + EXPECT_MATH_ERRNO(ERANGE); + + EXPECT_FP_EQ_WITH_EXCEPTION( + zero, + LIBC_NAMESPACE::expbf16(LIBC_NAMESPACE::fputil::cast(-93.0f)), + FE_UNDERFLOW | FE_INEXACT); + EXPECT_MATH_ERRNO(ERANGE); +}