diff --git a/libc/config/gpu/amdgpu/entrypoints.txt b/libc/config/gpu/amdgpu/entrypoints.txt index 7aaf3470dcea..de05977d89ee 100644 --- a/libc/config/gpu/amdgpu/entrypoints.txt +++ b/libc/config/gpu/amdgpu/entrypoints.txt @@ -532,6 +532,7 @@ if(LIBC_TYPES_HAS_FLOAT16) libc.src.math.coshf16 libc.src.math.cospif16 libc.src.math.erff16 + libc.src.math.erfcf16 libc.src.math.exp10f16 libc.src.math.exp10m1f16 libc.src.math.exp2f16 diff --git a/libc/config/gpu/nvptx/entrypoints.txt b/libc/config/gpu/nvptx/entrypoints.txt index 96c0902d729d..a0ce378c5b34 100644 --- a/libc/config/gpu/nvptx/entrypoints.txt +++ b/libc/config/gpu/nvptx/entrypoints.txt @@ -534,6 +534,7 @@ if(LIBC_TYPES_HAS_FLOAT16) libc.src.math.coshf16 libc.src.math.cospif16 libc.src.math.erff16 + libc.src.math.erfcf16 libc.src.math.exp10f16 libc.src.math.exp10m1f16 libc.src.math.exp2f16 diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index e7a452ec03ca..a37c27607db6 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -691,6 +691,7 @@ if(LIBC_TYPES_HAS_FLOAT16) libc.src.math.copysignf16 libc.src.math.cospif16 libc.src.math.erff16 + libc.src.math.erfcf16 libc.src.math.expf16 libc.src.math.f16add libc.src.math.f16addf diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt index a102fc07a2c0..66fb01e198ec 100644 --- a/libc/config/linux/riscv/entrypoints.txt +++ b/libc/config/linux/riscv/entrypoints.txt @@ -705,6 +705,7 @@ if(LIBC_TYPES_HAS_FLOAT16) libc.src.math.coshf16 libc.src.math.cospif16 libc.src.math.erff16 + libc.src.math.erfcf16 libc.src.math.exp10f16 libc.src.math.exp10m1f16 libc.src.math.exp2f16 diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index af54217b190f..83c494932fec 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -759,6 +759,7 @@ if(LIBC_TYPES_HAS_FLOAT16) libc.src.math.coshf16 libc.src.math.cospif16 libc.src.math.erff16 + libc.src.math.erfcf16 libc.src.math.exp10f16 libc.src.math.exp10m1f16 libc.src.math.exp2f16 diff --git a/libc/include/math.yaml b/libc/include/math.yaml index 8cf29c1a8cff..055149a6a4e3 100644 --- a/libc/include/math.yaml +++ b/libc/include/math.yaml @@ -403,6 +403,13 @@ functions: arguments: - type: _Float16 guard: LIBC_TYPES_HAS_FLOAT16 + - name: erfcf16 + standards: + - stdc + return_type: _Float16 + arguments: + - type: _Float16 + guard: LIBC_TYPES_HAS_FLOAT16 - name: exp standards: - stdc diff --git a/libc/shared/math.h b/libc/shared/math.h index 6add9628cabd..71da66d37bab 100644 --- a/libc/shared/math.h +++ b/libc/shared/math.h @@ -81,6 +81,7 @@ #include "math/dfmaf128.h" #include "math/dfmal.h" #include "math/dsqrtl.h" +#include "math/erfcf16.h" #include "math/erff.h" #include "math/erff16.h" #include "math/exp.h" diff --git a/libc/shared/math/erfcf16.h b/libc/shared/math/erfcf16.h new file mode 100644 index 000000000000..779401fe5dc0 --- /dev/null +++ b/libc/shared/math/erfcf16.h @@ -0,0 +1,27 @@ +//===-- Shared erfcf16 function ---------------------------------*- 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_SHARED_MATH_ERFCF16_H +#define LLVM_LIBC_SHARED_MATH_ERFCF16_H + +#ifdef LIBC_TYPES_HAS_FLOAT16 + +#include "shared/libc_common.h" +#include "src/__support/math/erfcf16.h" + +namespace LIBC_NAMESPACE_DECL { +namespace shared { + +using math::erfcf16; + +} // namespace shared +} // namespace LIBC_NAMESPACE_DECL + +#endif // LIBC_TYPES_HAS_FLOAT16 + +#endif // LLVM_LIBC_SHARED_MATH_ERFCF16_H diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt index 47e1f61be590..b607c351f3bc 100644 --- a/libc/src/__support/math/CMakeLists.txt +++ b/libc/src/__support/math/CMakeLists.txt @@ -960,6 +960,20 @@ add_header_library( libc.src.__support.macros.properties.cpu_features ) +add_header_library( + erfcf16 + HDRS + erfcf16.h + DEPENDS + 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.macros.config + libc.src.__support.macros.optimization +) + add_header_library( erff HDRS diff --git a/libc/src/__support/math/erfcf16.h b/libc/src/__support/math/erfcf16.h new file mode 100644 index 000000000000..6fc37d7467de --- /dev/null +++ b/libc/src/__support/math/erfcf16.h @@ -0,0 +1,145 @@ +//===-- Implementation header for erfcf16 -----------------------*- 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___SUPPORT_MATH_ERFCF16_H +#define LLVM_LIBC_SRC___SUPPORT_MATH_ERFCF16_H + +#include "include/llvm-libc-macros/float16-macros.h" + +#ifdef LIBC_TYPES_HAS_FLOAT16 + +#include "src/__support/FPUtil/FEnvImpl.h" +#include "src/__support/FPUtil/FPBits.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/macros/config.h" +#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY + +namespace LIBC_NAMESPACE_DECL { + +namespace math { + +LIBC_INLINE float16 erfcf16(float16 x) { + // Polynomials approximating erfc(|x|) on ( k/2, (k+1)/2 ) generated by + // Sollya with: > P = fpminimax(erfc(x), [|0, 1, 2, 3, 4, 5, 6, 7|], [|D...|], + // [k/2, (k+1)/2]); + // + // for k = 0..7. + constexpr double COEFFS[8][8] = { + {0x1.00000006e5898p0, -0x1.20dd7b4435481p0, 0x1.e2ffc0264c37cp-17, + 0x1.80ef5566d3135p-2, 0x1.96ef459387c13p-10, -0x1.e6f847a52d3fep-4, + 0x1.9a59af64a5dfap-7, 0x1.f652f3b14963bp-7}, + {0x1.001a9f1d3c0b4p0, -0x1.221a42637ae8p0, 0x1.9c3ddc1e5d176p-6, + 0x1.347481936316bp-2, 0x1.1db66a2746b8bp-3, -0x1.1d7a264182166p-2, + 0x1.ef858095e1609p-4, -0x1.26936198d6b07p-6}, + {0x1.f08a38741577bp-1, -0x1.e26ae90822fp-1, -0x1.f300a88478724p-2, + 0x1.115a0580ba3f8p0, -0x1.1a1c2bc3ffcdap-1, 0x1.888d0c9a082b8p-4, + 0x1.f41c9f1877fdfp-8, -0x1.a72fb878df30bp-9}, + {0x1.c1cabe622be64p-1, -0x1.ee19257d1037ap-2, -0x1.7a725229a24b8p0, + 0x1.2089bc62b1069p1, -0x1.673f1b9fbced5p0, 0x1.da7db686b0475p-2, + -0x1.49a292662ca6ap-4, 0x1.7e2b298da504dp-8}, + {0x1.a466e958ca525p0, -0x1.8a850d50339a9p1, 0x1.29b741ccfdd3ap1, + -0x1.b250e97982f77p-1, 0x1.ea6896c5fa419p-4, 0x1.b332978509bf1p-7, + -0x1.9f1c5d9122108p-8, 0x1.2fb4d83883203p-11}, + {0x1.12fb16acc5644p1, -0x1.25003c37e1b24p2, 0x1.0e0dc863b2f12p2, + -0x1.16f179d8797a6p1, 0x1.5c8b140bf43f8p-1, -0x1.07473d994c2edp-3, + 0x1.bd0c1c2d2a9e3p-7, -0x1.448767255aabbp-11}, + {0x1.13a691333e22ap0, -0x1.0e2a642d8d318p1, 0x1.c7d88193df94bp0, + -0x1.acf7c7b79498fp-1, 0x1.e626f6202a127p-3, -0x1.4babcc8609859p-5, + 0x1.f860060a3658p-9, -0x1.49a4190580d4p-13}, + {0x1.cf3a84bf655afp-3, -0x1.968e6988b5cep-2, 0x1.326f1f9499739p-2, + -0x1.011785d112c9bp-3, 0x1.0343a0c05e285p-5, -0x1.3a3ae5e48130ep-8, + 0x1.a7c5af0484892p-12, -0x1.ea82a062010c3p-17}, + }; + + static constexpr size_t N_ERFCF16_EXCEPTS = 3; + static constexpr fputil::ExceptValues + ERFCF16_EXCEPTS = {{ + // (input, RZ output, RU offset, RD offset, RN offset) + // x = 0x0.000216, erfc(x) = 0x1.000 + {0x0B17, 0x3BFF, 1, 0, 0}, + // x = 0x0.00346, erfc(x) = 0x0.ff8 + {0x1B17, 0x3BF7, 1, 0, 1}, + // x = -0x0.00346, erfc(x) = 0x1.00c + {0x9B17, 0x3C04, 1, 0, 0}, + }}; + using FPBits = typename fputil::FPBits; + FPBits xbits(x); + uint16_t x_u = xbits.uintval(); + + if (auto r = ERFCF16_EXCEPTS.lookup(x_u); LIBC_UNLIKELY(r.has_value())) + return r.value(); + + uint16_t x_abs = xbits.abs().uintval(); + + // Special cases: NaN and Inf + if (LIBC_UNLIKELY(x_abs >= 0x7c00U)) { + if (x_abs > 0x7c00U) { + if (xbits.is_signaling_nan()) { + fputil::raise_except_if_required(FE_INVALID); + return FPBits::quiet_nan().get_val(); + } + return x; + } + // erfc(+Inf) = 0, erfc(-Inf) = 2 + return xbits.is_neg() ? fputil::cast(2.0) + : fputil::cast(0.0); + } + + if (LIBC_UNLIKELY(x_abs == 0)) + return 1.0f16; + + // Asymptotic behavior: erfc(x) rounds to 0 or 2 for |x| >= 4.0. + if (LIBC_UNLIKELY(x_abs >= 0x4400U)) { // |x| >= 4.0 + if (xbits.is_neg()) { + // 0x1.0p-12 is ~0.25 ULP of 2.0 in float16, small enough to round + // to 2.0 in RN, but large enough to round down in RD/RZ. + float xf = fputil::cast(x); + return fputil::cast(2.0 - 0x1.0p-12 * (-4.0 / xf)); + } + fputil::set_errno_if_required(ERANGE); + fputil::raise_except_if_required(FE_UNDERFLOW | FE_INEXACT); + if (fputil::fenv_is_round_up()) + return FPBits::min_subnormal().get_val(); + return 0.0f16; + } + + // Polynomial approximation: + // erfc(x) ~ erfc(|x|) if x >= 0 + // erfc(x) ~ 2 - erfc(|x|) if x < 0 + // erfc(|x|) is evaluated using a degree-7 polynomial on each sub-interval. + + int idx = static_cast(xbits.abs().get_val() * 2.0f); + double xd = fputil::cast(xbits.abs().get_val()); + double xsq = xd * xd; + double x4 = xsq * xsq; + + double p01 = fputil::multiply_add(xd, COEFFS[idx][1], COEFFS[idx][0]); + double p23 = fputil::multiply_add(xd, COEFFS[idx][3], COEFFS[idx][2]); + double p45 = fputil::multiply_add(xd, COEFFS[idx][5], COEFFS[idx][4]); + double p67 = fputil::multiply_add(xd, COEFFS[idx][7], COEFFS[idx][6]); + + double p03 = fputil::multiply_add(xsq, p23, p01); + double p47 = fputil::multiply_add(xsq, p67, p45); + + double erfc_abs = fputil::multiply_add(x4, p47, p03); + + if (xbits.is_neg()) + return fputil::cast(2.0 - erfc_abs); + + return fputil::cast(erfc_abs); +} + +} // namespace math + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LIBC_TYPES_HAS_FLOAT16 + +#endif // LLVM_LIBC_SRC___SUPPORT_MATH_ERFCF16_H diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt index 5bdb8d99eede..d15ed5256c3b 100644 --- a/libc/src/math/CMakeLists.txt +++ b/libc/src/math/CMakeLists.txt @@ -142,6 +142,8 @@ add_math_entrypoint_object(erf) add_math_entrypoint_object(erff) add_math_entrypoint_object(erff16) +add_math_entrypoint_object(erfcf16) + add_math_entrypoint_object(exp) add_math_entrypoint_object(expf) add_math_entrypoint_object(expf16) diff --git a/libc/src/math/erfcf16.h b/libc/src/math/erfcf16.h new file mode 100644 index 000000000000..9f6f983e7000 --- /dev/null +++ b/libc/src/math/erfcf16.h @@ -0,0 +1,21 @@ +//===-- Implementation header for erfcf16 -----------------------*- 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_ERFCF16_H +#define LLVM_LIBC_SRC_MATH_ERFCF16_H + +#include "src/__support/macros/config.h" +#include "src/__support/macros/properties/types.h" + +namespace LIBC_NAMESPACE_DECL { + +float16 erfcf16(float16 x); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_MATH_ERFCF16_H diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt index 40d21ffaaa52..624c3b4f6795 100644 --- a/libc/src/math/generic/CMakeLists.txt +++ b/libc/src/math/generic/CMakeLists.txt @@ -1292,7 +1292,16 @@ add_entrypoint_object( ../erff16.h DEPENDS libc.src.__support.math.erff16 - libc.src.errno.errno +) + +add_entrypoint_object( + erfcf16 + SRCS + erfcf16.cpp + HDRS + ../erfcf16.h + DEPENDS + libc.src.__support.math.erfcf16 ) add_entrypoint_object( diff --git a/libc/src/math/generic/erfcf16.cpp b/libc/src/math/generic/erfcf16.cpp new file mode 100644 index 000000000000..a1f7397c3b58 --- /dev/null +++ b/libc/src/math/generic/erfcf16.cpp @@ -0,0 +1,16 @@ +//===-- Half-precision erfc(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/erfcf16.h" +#include "src/__support/math/erfcf16.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(float16, erfcf16, (float16 x)) { return math::erfcf16(x); } + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/test/shared/CMakeLists.txt b/libc/test/shared/CMakeLists.txt index 9ea5d1bd359c..b807ea9ac602 100644 --- a/libc/test/shared/CMakeLists.txt +++ b/libc/test/shared/CMakeLists.txt @@ -80,6 +80,7 @@ add_fp_unittest( libc.src.__support.math.dsqrtl libc.src.__support.math.exp10m1f libc.src.__support.math.exp10m1f16 + libc.src.__support.math.erfcf16 libc.src.__support.math.erff libc.src.__support.math.erff16 libc.src.__support.math.exp diff --git a/libc/test/shared/shared_math_test.cpp b/libc/test/shared/shared_math_test.cpp index 5afb982dd57e..31b14bbe204c 100644 --- a/libc/test/shared/shared_math_test.cpp +++ b/libc/test/shared/shared_math_test.cpp @@ -32,6 +32,7 @@ TEST(LlvmLibcSharedMathTest, AllFloat16) { EXPECT_FP_EQ(0x1p+0f16, LIBC_NAMESPACE::shared::coshf16(0.0f16)); EXPECT_FP_EQ(0x1p+0f16, LIBC_NAMESPACE::shared::cospif16(0.0f16)); EXPECT_FP_EQ(0x0p+0f16, LIBC_NAMESPACE::shared::erff16(0.0f16)); + EXPECT_FP_EQ(0x1p+0f16, LIBC_NAMESPACE::shared::erfcf16(0.0f)); EXPECT_FP_EQ(0x1p+0f16, LIBC_NAMESPACE::shared::exp10f16(0.0f16)); EXPECT_FP_EQ(0x0p+0f16, LIBC_NAMESPACE::shared::exp10m1f16(0.0f16)); EXPECT_FP_EQ(0x1p+0f16, LIBC_NAMESPACE::shared::exp2f16(0.0f16)); diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt index ceed946e2386..0e93760a4675 100644 --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -2723,6 +2723,18 @@ add_fp_unittest( libc.src.__support.FPUtil.fp_bits ) +add_fp_unittest( + erfcf16_test + NEED_MPFR + SUITE + libc-math-unittests + SRCS + erfcf16_test.cpp + DEPENDS + libc.src.math.erfcf16 + libc.src.__support.FPUtil.fp_bits +) + add_fp_unittest( pow_test NEED_MPFR diff --git a/libc/test/src/math/erfcf16_test.cpp b/libc/test/src/math/erfcf16_test.cpp new file mode 100644 index 000000000000..d06bca5dc23a --- /dev/null +++ b/libc/test/src/math/erfcf16_test.cpp @@ -0,0 +1,43 @@ +//===-- Exhaustive test for erfcf16 ---------------------------------------===// +// +// 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/macros/optimization.h" +#include "src/math/erfcf16.h" +#include "test/UnitTest/FPMatcher.h" +#include "test/UnitTest/Test.h" +#include "utils/MPFRWrapper/MPFRUtils.h" + +using LlvmLibcErfcf16Test = LIBC_NAMESPACE::testing::FPTest; + +namespace mpfr = LIBC_NAMESPACE::testing::mpfr; + +// Range: [0, Inf]; +// 0x0000 is +0.0, 0x7c00 is +Inf. +static constexpr uint16_t POS_START = 0x0000U; +static constexpr uint16_t POS_STOP = 0x7c00U; + +// Range: [-0, -Inf]; +// 0x8000 is -0.0, 0xfc00 is -Inf. +static constexpr uint16_t NEG_START = 0x8000U; +static constexpr uint16_t NEG_STOP = 0xfc00U; + +TEST_F(LlvmLibcErfcf16Test, PositiveRange) { + for (uint16_t v = POS_START; v <= POS_STOP; ++v) { + float16 x = FPBits(v).get_val(); + EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Erfc, x, + LIBC_NAMESPACE::erfcf16(x), 0.5); + } +} + +TEST_F(LlvmLibcErfcf16Test, NegativeRange) { + for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) { + float16 x = FPBits(v).get_val(); + EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Erfc, x, + LIBC_NAMESPACE::erfcf16(x), 0.5); + } +} diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt index 9d97c90cfaeb..c2ac29622c32 100644 --- a/libc/test/src/math/smoke/CMakeLists.txt +++ b/libc/test/src/math/smoke/CMakeLists.txt @@ -5183,6 +5183,17 @@ add_fp_unittest( libc.src.math.erff16 ) +add_fp_unittest( + erfcf16_test + SUITE + libc-math-smoke-tests + SRCS + erfcf16_test.cpp + DEPENDS + libc.hdr.errno_macros + libc.src.math.erfcf16 +) + add_fp_unittest( pow_test SUITE diff --git a/libc/test/src/math/smoke/erfcf16_test.cpp b/libc/test/src/math/smoke/erfcf16_test.cpp new file mode 100644 index 000000000000..22df1bcbb0d2 --- /dev/null +++ b/libc/test/src/math/smoke/erfcf16_test.cpp @@ -0,0 +1,66 @@ +//===-- Unittests for erfcf16 ---------------------------------------------===// +// +// 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/math_macros.h" +#include "hdr/stdint_proxy.h" +#include "src/math/erfcf16.h" +#include "test/UnitTest/FPMatcher.h" +#include "test/UnitTest/Test.h" + +using LlvmLibcErfcTest = LIBC_NAMESPACE::testing::FPTest; + +TEST_F(LlvmLibcErfcTest, SpecialNumbers) { + EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::erfcf16(sNaN), FE_INVALID); + EXPECT_MATH_ERRNO(0); + + EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::erfcf16(aNaN)); + EXPECT_FP_EQ_ALL_ROUNDING(0.0f16, LIBC_NAMESPACE::erfcf16(inf)); + EXPECT_FP_EQ_ALL_ROUNDING(2.0f16, LIBC_NAMESPACE::erfcf16(neg_inf)); + EXPECT_FP_EQ_ALL_ROUNDING(1.0f16, LIBC_NAMESPACE::erfcf16(zero)); + EXPECT_FP_EQ_ALL_ROUNDING(1.0f16, LIBC_NAMESPACE::erfcf16(neg_zero)); +} + +TEST_F(LlvmLibcErfcTest, Underflow) { + EXPECT_FP_EQ_WITH_EXCEPTION(zero, LIBC_NAMESPACE::erfcf16(4.0f16), + FE_UNDERFLOW | FE_INEXACT); + EXPECT_MATH_ERRNO(ERANGE); + + EXPECT_FP_EQ_WITH_EXCEPTION(zero, LIBC_NAMESPACE::erfcf16(5.75f16), + FE_UNDERFLOW | FE_INEXACT); + EXPECT_MATH_ERRNO(ERANGE); + + EXPECT_FP_EQ_WITH_EXCEPTION(zero, LIBC_NAMESPACE::erfcf16(11.25f16), + FE_UNDERFLOW | FE_INEXACT); + EXPECT_MATH_ERRNO(ERANGE); +} + +#ifdef LIBC_TEST_FTZ_DAZ + +using namespace LIBC_NAMESPACE::testing; + +TEST_F(LlvmLibcErfcTest, FTZMode) { + ModifyMXCSR mxcsr(FTZ); + EXPECT_FP_EQ(1.0f16, LIBC_NAMESPACE::erfcf16(min_denormal)); + EXPECT_FP_EQ(1.0f16, LIBC_NAMESPACE::erfcf16(max_denormal)); +} + +TEST_F(LlvmLibcErfcTest, DAZMode) { + ModifyMXCSR mxcsr(DAZ); + EXPECT_FP_EQ(1.0f16, LIBC_NAMESPACE::erfcf16(min_denormal)); + EXPECT_FP_EQ(1.0f16, LIBC_NAMESPACE::erfcf16(max_denormal)); +} + +TEST_F(LlvmLibcErfcTest, FTZDAZMode) { + ModifyMXCSR mxcsr(FTZ | DAZ); + + EXPECT_FP_EQ(1.0f16, LIBC_NAMESPACE::erfcf16(min_denormal)); + EXPECT_FP_EQ(1.0f16, LIBC_NAMESPACE::erfcf16(max_denormal)); +} + +#endif diff --git a/libc/utils/MPFRWrapper/MPCommon.cpp b/libc/utils/MPFRWrapper/MPCommon.cpp index 47d15b3981ef..9fa50b9187df 100644 --- a/libc/utils/MPFRWrapper/MPCommon.cpp +++ b/libc/utils/MPFRWrapper/MPCommon.cpp @@ -210,6 +210,12 @@ MPFRNumber MPFRNumber::erf() const { return result; } +MPFRNumber MPFRNumber::erfc() const { + MPFRNumber result(*this); + mpfr_erfc(result.value, value, mpfr_rounding); + return result; +} + MPFRNumber MPFRNumber::exp() const { MPFRNumber result(*this); mpfr_exp(result.value, value, mpfr_rounding); diff --git a/libc/utils/MPFRWrapper/MPCommon.h b/libc/utils/MPFRWrapper/MPCommon.h index cb39735ca0bc..b8d144e1582b 100644 --- a/libc/utils/MPFRWrapper/MPCommon.h +++ b/libc/utils/MPFRWrapper/MPCommon.h @@ -200,6 +200,7 @@ public: MPFRNumber cosh() const; MPFRNumber cospi() const; MPFRNumber erf() const; + MPFRNumber erfc() const; MPFRNumber exp() const; MPFRNumber exp2() const; MPFRNumber exp2m1() const; diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp index d9f444cfa8a7..1f6396221664 100644 --- a/libc/utils/MPFRWrapper/MPFRUtils.cpp +++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp @@ -59,6 +59,8 @@ unary_operation(Operation op, InputType input, unsigned int precision, return mpfrInput.cospi(); case Operation::Erf: return mpfrInput.erf(); + case Operation::Erfc: + return mpfrInput.erfc(); case Operation::Exp: return mpfrInput.exp(); case Operation::Exp2: diff --git a/libc/utils/MPFRWrapper/MPFRUtils.h b/libc/utils/MPFRWrapper/MPFRUtils.h index 6c24b66324c1..9bfc5be507a5 100644 --- a/libc/utils/MPFRWrapper/MPFRUtils.h +++ b/libc/utils/MPFRWrapper/MPFRUtils.h @@ -40,6 +40,7 @@ enum class Operation : int { Cosh, Cospi, Erf, + Erfc, Exp, Exp2, Exp2m1, diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel index 7358d97636a1..925670785d19 100644 --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -3830,6 +3830,21 @@ libc_support_library( ], ) +libc_support_library( + name = "__support_math_erfcf16", + hdrs = ["src/__support/math/erfcf16.h"], + deps = [ + ":__support_fputil_cast", + ":__support_fputil_except_value_utils", + ":__support_fputil_fenv_impl", + ":__support_fputil_fp_bits", + ":__support_fputil_multiply_add", + ":__support_macros_config", + ":__support_macros_optimization", + ":llvm_libc_macros_float16_macros", + ], +) + libc_support_library( name = "__support_math_exp_float_constants", hdrs = ["src/__support/math/exp_float_constants.h"], @@ -6769,6 +6784,13 @@ libc_math_function( ], ) +libc_math_function( + name = "erfcf16", + additional_deps = [ + ":__support_math_erfcf16", + ], +) + libc_math_function( name = "exp", additional_deps = [