[libc-commits] [libc] e06b5e5 - [libc][math] Implement C23 half precision erfc function (#180930)

via libc-commits libc-commits at lists.llvm.org
Fri Mar 27 07:42:08 PDT 2026


Author: Anonmiraj
Date: 2026-03-27T10:42:03-04:00
New Revision: e06b5e53a29b394eee91b0dd1e227bc707d547e9

URL: https://github.com/llvm/llvm-project/commit/e06b5e53a29b394eee91b0dd1e227bc707d547e9
DIFF: https://github.com/llvm/llvm-project/commit/e06b5e53a29b394eee91b0dd1e227bc707d547e9.diff

LOG: [libc][math] Implement C23 half precision erfc function (#180930)

Add support for the half-precision complementary error function
`erfcf16``, using a Sollya generated polynomial implementation with
proper handling of special cases.

Extend the MPFR utilities with erfc support to allow tests.

closes: #180927

Added: 
    libc/shared/math/erfcf16.h
    libc/src/__support/math/erfcf16.h
    libc/src/math/erfcf16.h
    libc/src/math/generic/erfcf16.cpp
    libc/test/src/math/erfcf16_test.cpp
    libc/test/src/math/smoke/erfcf16_test.cpp

Modified: 
    libc/config/gpu/amdgpu/entrypoints.txt
    libc/config/gpu/nvptx/entrypoints.txt
    libc/config/linux/aarch64/entrypoints.txt
    libc/config/linux/riscv/entrypoints.txt
    libc/config/linux/x86_64/entrypoints.txt
    libc/include/math.yaml
    libc/shared/math.h
    libc/src/__support/math/CMakeLists.txt
    libc/src/math/CMakeLists.txt
    libc/src/math/generic/CMakeLists.txt
    libc/test/shared/CMakeLists.txt
    libc/test/shared/shared_math_test.cpp
    libc/test/src/math/CMakeLists.txt
    libc/test/src/math/smoke/CMakeLists.txt
    libc/utils/MPFRWrapper/MPCommon.cpp
    libc/utils/MPFRWrapper/MPCommon.h
    libc/utils/MPFRWrapper/MPFRUtils.cpp
    libc/utils/MPFRWrapper/MPFRUtils.h
    utils/bazel/llvm-project-overlay/libc/BUILD.bazel

Removed: 
    


################################################################################
diff  --git a/libc/config/gpu/amdgpu/entrypoints.txt b/libc/config/gpu/amdgpu/entrypoints.txt
index 7aaf3470dceab..de05977d89ee8 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 96c0902d729dc..a0ce378c5b346 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 e7a452ec03ca4..a37c27607db69 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 a102fc07a2c0d..66fb01e198ecc 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 af54217b190fc..83c494932fec7 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 8cf29c1a8cff1..055149a6a4e3c 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 6add9628cabdd..71da66d37baba 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 0000000000000..779401fe5dc09
--- /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 47e1f61be590b..b607c351f3bc5 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 0000000000000..6fc37d7467de7
--- /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<float16, N_ERFCF16_EXCEPTS>
+      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<float16>;
+  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<float16>(2.0)
+                          : fputil::cast<float16>(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<float>(x);
+      return fputil::cast<float16>(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<int>(xbits.abs().get_val() * 2.0f);
+  double xd = fputil::cast<double>(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<float16>(2.0 - erfc_abs);
+
+  return fputil::cast<float16>(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 5bdb8d99eedeb..d15ed5256c3bf 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 0000000000000..9f6f983e7000d
--- /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 40d21ffaaa524..624c3b4f67951 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 0000000000000..a1f7397c3b58c
--- /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 9ea5d1bd359c7..b807ea9ac602f 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 5afb982dd57ed..31b14bbe204c7 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 ceed946e2386e..0e93760a46753 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 0000000000000..d06bca5dc23a6
--- /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<float16>;
+
+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 9d97c90cfaeb9..c2ac29622c32a 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 0000000000000..22df1bcbb0d29
--- /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<float16>;
+
+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 47d15b3981efe..9fa50b9187dff 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 cb39735ca0bc6..b8d144e1582b4 100644
--- a/libc/utils/MPFRWrapper/MPCommon.h
+++ b/libc/utils/MPFRWrapper/MPCommon.h
@@ -200,6 +200,7 @@ class MPFRNumber {
   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 d9f444cfa8a76..1f63962216647 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 6c24b66324c1a..9bfc5be507a59 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 7358d97636a1c..925670785d19c 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 = [


        


More information about the libc-commits mailing list