diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 0daf3ccacbb270..eb7d3883a8f711 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -611,6 +611,7 @@ if(LIBC_TYPES_HAS_FLOAT16) libc.src.math.copysignf16 libc.src.math.exp10f16 libc.src.math.exp2f16 + libc.src.math.exp2m1f16 libc.src.math.expf16 libc.src.math.expm1f16 libc.src.math.f16add diff --git a/libc/docs/math/index.rst b/libc/docs/math/index.rst index 0e757e7978bfa5..806dcc64bb9396 100644 --- a/libc/docs/math/index.rst +++ b/libc/docs/math/index.rst @@ -296,7 +296,7 @@ Higher Math Functions +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ | exp2 | |check| | |check| | | |check| | | 7.12.6.4 | F.10.3.4 | +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ -| exp2m1 | |check| | | | | | 7.12.6.5 | F.10.3.5 | +| exp2m1 | |check| | | | |check| | | 7.12.6.5 | F.10.3.5 | +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ | expm1 | |check| | |check| | | |check| | | 7.12.6.6 | F.10.3.6 | +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ diff --git a/libc/newhdrgen/yaml/math.yaml b/libc/newhdrgen/yaml/math.yaml index 04b6a073deace0..d8b810b542cb9f 100644 --- a/libc/newhdrgen/yaml/math.yaml +++ b/libc/newhdrgen/yaml/math.yaml @@ -284,6 +284,13 @@ functions: return_type: float arguments: - type: float + - name: exp2m1f16 + standards: + - stdc + return_type: _Float16 + arguments: + - type: _Float16 + guard: LIBC_TYPES_HAS_FLOAT16 - name: expf standards: - stdc diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td index dadd1ef11d7d3f..1b255690c2e453 100644 --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -682,6 +682,7 @@ def StdC : StandardSpec<"stdc"> { GuardedFunctionSpec<"exp2f16", RetValSpec, [ArgSpec], "LIBC_TYPES_HAS_FLOAT16">, FunctionSpec<"exp2m1f", RetValSpec, [ArgSpec]>, + GuardedFunctionSpec<"exp2m1f16", RetValSpec, [ArgSpec], "LIBC_TYPES_HAS_FLOAT16">, FunctionSpec<"expm1", RetValSpec, [ArgSpec]>, FunctionSpec<"expm1f", RetValSpec, [ArgSpec]>, diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt index 97e2d0d64a147b..53907e47323e9e 100644 --- a/libc/src/math/CMakeLists.txt +++ b/libc/src/math/CMakeLists.txt @@ -121,6 +121,7 @@ add_math_entrypoint_object(exp2f) add_math_entrypoint_object(exp2f16) add_math_entrypoint_object(exp2m1f) +add_math_entrypoint_object(exp2m1f16) add_math_entrypoint_object(exp10) add_math_entrypoint_object(exp10f) diff --git a/libc/src/math/exp2m1f16.h b/libc/src/math/exp2m1f16.h new file mode 100644 index 00000000000000..39fcebd5a69300 --- /dev/null +++ b/libc/src/math/exp2m1f16.h @@ -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 diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt index 627168ee136997..b58935abdf056d 100644 --- a/libc/src/math/generic/CMakeLists.txt +++ b/libc/src/math/generic/CMakeLists.txt @@ -1500,14 +1500,10 @@ add_entrypoint_object( .expxf16 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.macros.optimization COMPILE_OPTIONS @@ -1535,6 +1531,30 @@ 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.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.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( exp10 SRCS @@ -5235,7 +5255,9 @@ add_header_library( expxf16.h DEPENDS libc.src.__support.CPP.array + 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.macros.attributes ) diff --git a/libc/src/math/generic/exp2f16.cpp b/libc/src/math/generic/exp2f16.cpp index 3c4310259b1df9..858053fe2bccba 100644 --- a/libc/src/math/generic/exp2f16.cpp +++ b/libc/src/math/generic/exp2f16.cpp @@ -10,14 +10,10 @@ #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/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" @@ -89,39 +85,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(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(x_hi << fputil::FPBits::FRACTION_LEN); - float exp2_hi_mid = fputil::FPBits(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 fputil::cast(exp2_hi_mid * exp2_lo); } diff --git a/libc/src/math/generic/exp2m1f16.cpp b/libc/src/math/generic/exp2m1f16.cpp new file mode 100644 index 00000000000000..e6c57d730e3c2d --- /dev/null +++ b/libc/src/math/generic/exp2m1f16.cpp @@ -0,0 +1,162 @@ +//===-- 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/cast.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 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 + 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; + 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( + fputil::cast(-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 -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 fputil::cast( + 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 fputil::cast( + fputil::multiply_add(exp2_hi_mid, exp2_lo, -1.0f)); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/math/generic/expxf16.h b/libc/src/math/generic/expxf16.h index a0db6cee438e92..35294130a15007 100644 --- a/libc/src/math/generic/expxf16.h +++ b/libc/src/math/generic/expxf16.h @@ -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" @@ -89,6 +90,43 @@ constexpr cpp::array 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(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(x_hi << fputil::FPBits::FRACTION_LEN); + float exp2_hi_mid = fputil::FPBits(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}; +} + } // namespace LIBC_NAMESPACE_DECL #endif // LLVM_LIBC_SRC_MATH_GENERIC_EXPXF16_H diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt index 0c4118c3694548..07a9405081f97d 100644 --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -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 diff --git a/libc/test/src/math/exp2m1f16_test.cpp b/libc/test/src/math/exp2m1f16_test.cpp new file mode 100644 index 00000000000000..bf299118429d7e --- /dev/null +++ b/libc/test/src/math/exp2m1f16_test.cpp @@ -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; + +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); + } +} diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt index a70b65f23f4b71..c4787229c3ec15 100644 --- a/libc/test/src/math/smoke/CMakeLists.txt +++ b/libc/test/src/math/smoke/CMakeLists.txt @@ -1175,6 +1175,18 @@ add_fp_unittest( libc.src.math.exp2m1f ) +add_fp_unittest( + exp2m1f16_test + SUITE + libc-math-smoke-tests + SRCS + exp2m1f16_test.cpp + DEPENDS + libc.hdr.fenv_macros + libc.src.errno.errno + libc.src.math.exp2m1f16 +) + add_fp_unittest( exp10_test SUITE diff --git a/libc/test/src/math/smoke/exp2m1f16_test.cpp b/libc/test/src/math/smoke/exp2m1f16_test.cpp new file mode 100644 index 00000000000000..8be86973f32134 --- /dev/null +++ b/libc/test/src/math/smoke/exp2m1f16_test.cpp @@ -0,0 +1,87 @@ +//===-- 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/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; + +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(-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 = 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(-1.0, LIBC_NAMESPACE::exp2m1f16(neg_max_normal), + FE_INEXACT); + + EXPECT_FP_EQ_ALL_ROUNDING(-0x1.ffcp-1, LIBC_NAMESPACE::exp2m1f16(-11.0)); + + float16 x = -12.0; + + EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_NEAREST( + -1.0, LIBC_NAMESPACE::exp2m1f16(x), FE_INEXACT); + + EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_UPWARD( + -0x1.ffcp-1, LIBC_NAMESPACE::exp2m1f16(x), FE_INEXACT); + + EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_DOWNWARD( + -1.0, LIBC_NAMESPACE::exp2m1f16(x), FE_INEXACT); + + EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_TOWARD_ZERO( + -0x1.ffcp-1, LIBC_NAMESPACE::exp2m1f16(x), FE_INEXACT); +}