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

via libc-commits libc-commits at lists.llvm.org
Wed Feb 11 04:26:52 PST 2026


https://github.com/AnonMiraj created https://github.com/llvm/llvm-project/pull/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

>From 76b900af328b6288c43103efb729ac292670d895 Mon Sep 17 00:00:00 2001
From: Ezzeldin Ibrahim <ezzibrahimx at gmail.com>
Date: Wed, 11 Feb 2026 13:01:50 +0200
Subject: [PATCH] [libc][math] Implement C23 half precision erfc function

---
 libc/config/gpu/amdgpu/entrypoints.txt    |   1 +
 libc/config/gpu/nvptx/entrypoints.txt     |   1 +
 libc/config/linux/aarch64/entrypoints.txt |   1 +
 libc/config/linux/arm/entrypoints.txt     |   1 +
 libc/config/linux/riscv/entrypoints.txt   |   1 +
 libc/config/linux/x86_64/entrypoints.txt  |   1 +
 libc/config/windows/entrypoints.txt       |   1 +
 libc/include/math.yaml                    |   6 +
 libc/shared/math.h                        |   1 +
 libc/shared/math/erfcf16.h                |  27 +++
 libc/src/__support/math/CMakeLists.txt    |  12 ++
 libc/src/__support/math/erfcf16.h         | 197 ++++++++++++++++++++++
 libc/src/math/CMakeLists.txt              |   2 +
 libc/src/math/erfcf16.h                   |  21 +++
 libc/src/math/generic/CMakeLists.txt      |  11 ++
 libc/src/math/generic/erfcf16.cpp         |  16 ++
 libc/test/src/math/CMakeLists.txt         |  12 ++
 libc/test/src/math/erfcf16_test.cpp       |  43 +++++
 libc/test/src/math/smoke/CMakeLists.txt   |  10 ++
 libc/test/src/math/smoke/erfcf16_test.cpp |  51 ++++++
 libc/utils/MPFRWrapper/MPCommon.cpp       |   6 +
 libc/utils/MPFRWrapper/MPCommon.h         |   1 +
 libc/utils/MPFRWrapper/MPFRUtils.cpp      |   2 +
 libc/utils/MPFRWrapper/MPFRUtils.h        |   1 +
 24 files changed, 426 insertions(+)
 create mode 100644 libc/shared/math/erfcf16.h
 create mode 100644 libc/src/__support/math/erfcf16.h
 create mode 100644 libc/src/math/erfcf16.h
 create mode 100644 libc/src/math/generic/erfcf16.cpp
 create mode 100644 libc/test/src/math/erfcf16_test.cpp
 create mode 100644 libc/test/src/math/smoke/erfcf16_test.cpp

diff --git a/libc/config/gpu/amdgpu/entrypoints.txt b/libc/config/gpu/amdgpu/entrypoints.txt
index 0dda7d5c683ec..551fe8bd5693a 100644
--- a/libc/config/gpu/amdgpu/entrypoints.txt
+++ b/libc/config/gpu/amdgpu/entrypoints.txt
@@ -311,6 +311,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.dfmal
     libc.src.math.dmull
     libc.src.math.dsqrtl
+    libc.src.math.erfcf16
     libc.src.math.erff
     libc.src.math.exp
     libc.src.math.exp10
diff --git a/libc/config/gpu/nvptx/entrypoints.txt b/libc/config/gpu/nvptx/entrypoints.txt
index 6070fb5b17b3c..1cf9cb3fc9c7b 100644
--- a/libc/config/gpu/nvptx/entrypoints.txt
+++ b/libc/config/gpu/nvptx/entrypoints.txt
@@ -311,6 +311,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.dfmal
     libc.src.math.dmull
     libc.src.math.dsqrtl
+    libc.src.math.erfcf16
     libc.src.math.erff
     libc.src.math.exp
     libc.src.math.exp10
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 970c825bbfc96..8c65e1c7de90c 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -452,6 +452,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.dmull
     libc.src.math.dsqrtl
     libc.src.math.dsubl
+    libc.src.math.erfcf16
     libc.src.math.erff
     libc.src.math.exp
     libc.src.math.exp10
diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt
index f04ac40145d3a..ab5bebbdf7516 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -274,6 +274,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.coshf
     libc.src.math.dfmal
     libc.src.math.dsqrtl
+    libc.src.math.erfcf16
     libc.src.math.erff
     libc.src.math.exp
     libc.src.math.exp10
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 7baf4de9d8a5b..f9728ec3a4b13 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -456,6 +456,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.dmull
     libc.src.math.dsqrtl
     libc.src.math.dsubl
+    libc.src.math.erfcf16
     libc.src.math.erff
     libc.src.math.exp
     libc.src.math.exp10
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 9399b284fa2da..906e6699e5f16 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -498,6 +498,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.daddl
     libc.src.math.ddivl
     libc.src.math.dsubl
+    libc.src.math.erfcf16
     libc.src.math.erff
     libc.src.math.exp
     libc.src.math.exp10
diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt
index 3a76595b258e2..93eb84d9ac2b5 100644
--- a/libc/config/windows/entrypoints.txt
+++ b/libc/config/windows/entrypoints.txt
@@ -156,6 +156,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.ddivl
     libc.src.math.dfmal
     libc.src.math.dsubl
+    libc.src.math.erfcf16
     libc.src.math.erff
     libc.src.math.exp
     libc.src.math.expf
diff --git a/libc/include/math.yaml b/libc/include/math.yaml
index afd3ae33305c1..540925a71aa5a 100644
--- a/libc/include/math.yaml
+++ b/libc/include/math.yaml
@@ -396,6 +396,12 @@ functions:
     return_type: float
     arguments:
       - type: float
+  - name: erfcf16
+    standards:
+      - stdc
+    return_type: _Float16
+    arguments:
+      - type: _Float16
   - name: exp
     standards:
       - stdc
diff --git a/libc/shared/math.h b/libc/shared/math.h
index 4b0f2c67b0c86..b2e2632cbb0b0 100644
--- a/libc/shared/math.h
+++ b/libc/shared/math.h
@@ -42,6 +42,7 @@
 #include "math/dfmaf128.h"
 #include "math/dfmal.h"
 #include "math/dsqrtl.h"
+#include "math/erfcf16.h"
 #include "math/erff.h"
 #include "math/exp.h"
 #include "math/exp10.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 6705a2a08f3a7..a50884ef0432f 100644
--- a/libc/src/__support/math/CMakeLists.txt
+++ b/libc/src/__support/math/CMakeLists.txt
@@ -508,6 +508,18 @@ 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.fp_bits
+    libc.src.__support.FPUtil.multiply_add
+    libc.src.__support.FPUtil.polyeval
+    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..da9b67a6dbab4
--- /dev/null
+++ b/libc/src/__support/math/erfcf16.h
@@ -0,0 +1,197 @@
+//===-- 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/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/8, (k + 1)/8 ) generated by
+  // Sollya with: > P = fpminimax(erfc(x), [|0, 1, 2, 3, 4, 5, 6, 7|], [|D...|],
+  //                             [k/8, (k + 1)/8]);
+  // for k = 0..31.
+  constexpr double COEFFS[32][8] = {
+      {0x1.000000000001ap0, -0x1.20dd75042fe11p0, 0x1.eedc6f1918987p-31,
+       0x1.812743003751cp-2, 0x1.b64a20332eb3dp-20, -0x1.ce4a3020c5e8dp-4,
+       0x1.c23deea13dadap-13, 0x1.ab4a28f5e2202p-6},
+      {0x1.000000261e1a9p0, -0x1.20dd7ba6be17dp0, 0x1.feaa7936695dep-18,
+       0x1.8111aee222bb3p-2, 0x1.1e49370dc07e5p-11, -0x1.d7628d2f099dfp-4,
+       0x1.64a9012704b63p-8, 0x1.507b7d761dbbdp-6},
+      {0x1.00000e7d6c906p0, -0x1.20deeb74e7ceep0, 0x1.06dc8c97aa061p-12,
+       0x1.7f85253e7aa3fp-2, 0x1.9a4f9aedbda2ap-8, -0x1.06934bb956303p-3,
+       0x1.6f9b296bf2cp-6, 0x1.6725f01ebef61p-7},
+      {0x1.0000f0d17e652p0, -0x1.20eec49f1bb9fp0, 0x1.15bf700a3afecp-9,
+       0x1.7742179b9b76p-2, 0x1.bf4cf873dfcc7p-6, -0x1.4a7453b65895cp-3,
+       0x1.a7171b2b94f65p-5, -0x1.3db6226bfb07ep-13},
+      {0x1.00064f5d99b02p0, -0x1.21388015af14dp0, 0x1.1f337b73afb37p-7,
+       0x1.60d75bce880c8p-2, 0x1.21bee64bd2bfep-4, -0x1.b4c9b7cc4d861p-3,
+       0x1.6156b46d67517p-4, -0x1.4a6ad4a1a9a6dp-7},
+      {0x1.001646fa41f93p0, -0x1.21ea485326f4dp0, 0x1.64386e40081f9p-6,
+       0x1.3d6f1181dc1f2p-2, 0x1.025fb3fbb9694p-3, -0x1.111c22989270ep-2,
+       0x1.d6df1d011e6a5p-4, -0x1.11af42f321738p-6},
+      {0x1.00277226071fep0, -0x1.228d8a26ceba9p0, 0x1.056317058c132p-5,
+       0x1.25cda1a141e98p-2, 0x1.42d4218a6d87p-3, -0x1.2b8395261188dp-2,
+       0x1.038075602477dp-3, -0x1.3754fc0d6d03dp-6},
+      {0x1.ffdf656fd5544p-1, -0x1.20dbf015167a4p0, 0x1.426f915ba02ccp-7,
+       0x1.4fb39034f18e7p-2, 0x1.cb1896449b52p-4, -0x1.0c5791c048cfbp-2,
+       0x1.d8acdd7692448p-4, -0x1.19cd761c2fcbfp-6},
+      {0x1.fd52aab04431fp-1, -0x1.18084cdb7869fp0, -0x1.7b239d67be2f2p-4,
+       0x1.fce1bd937aa95p-2, -0x1.c7f53c1a8860cp-5, -0x1.4c0be3dd60746p-3,
+       0x1.511a1822165ccp-4, -0x1.997b355ae9313p-7},
+      {0x1.f630da3ca180fp-1, -0x1.01f9cdebb8a42p0, -0x1.48d5e3d9e3bbbp-2,
+       0x1.ab0c1d54fd42p-1, -0x1.6abcd4a981266p-2, -0x1.b62df36853d2ap-9,
+       0x1.219207ea8070cp-5, -0x1.acb65c612c3cbp-8},
+      {0x1.e8518d1829bdep-1, -0x1.b680bb7f5ca05p-1, -0x1.5dd957f485f26p-1,
+       0x1.50ed752c29101p0, -0x1.7a989bc66b636p-1, 0x1.73a7c29911c75p-3,
+       -0x1.c8f687bc29284p-7, -0x1.d9a2c5e81906dp-11},
+      {0x1.d455b8759924bp-1, -0x1.50d550bd0031ap-1, -0x1.1dd31217d772dp0,
+       0x1.d768acdc964e1p0, -0x1.1f325385cfea3p0, 0x1.64f5ef1e1b149p-2,
+       -0x1.becf19e32832cp-5, 0x1.b3d95b8249dc2p-9},
+      {0x1.c03fc3d1dfd52p-1, -0x1.e5be0a5eb8ea1p-2, -0x1.7c0ea4743a94ap0,
+       0x1.203ad2e0bb23dp1, -0x1.657e5f6a3a7bfp0, 0x1.d5e86ba1d05p-2,
+       -0x1.44444f5e3e2f1p-4, 0x1.7465b9882b39bp-8},
+      {0x1.b82e3874361f8p-1, -0x1.9f1c5ba9f25b5p-2, -0x1.9d2f419b75126p0,
+       0x1.317ed738c38e5p1, -0x1.7b17d3b23fc5cp0, 0x1.f656370d9a5a4p-2,
+       -0x1.5f519dd3bee19p-4, 0x1.9b165d0ccb0fep-8},
+      {0x1.cba592d21041ap-1, -0x1.1c9e2f4ac28efp-1, -0x1.5bcbe8415d66fp0,
+       0x1.12aa2de5d021cp1, -0x1.58324c5f23234p0, 0x1.c6ed4ac6a2edfp-2,
+       -0x1.3b86d3a3e85c9p-4, 0x1.6cc0ec77d11b9p-8},
+      {0x1.03caf33285512p0, -0x1.fb887e48a3eedp-1, -0x1.5431e46bb9a3fp-1,
+       0x1.87e44d6713845p0, -0x1.047b2b206c351p0, 0x1.5c0fe8541e1e5p-2,
+       -0x1.df700bfe6dee5p-5, 0x1.1089feecce9f6p-8},
+      {0x1.37baad7246b3cp0, -0x1.b333d66d61c0cp0, 0x1.9673eefbcd456p-2,
+       0x1.4b8e581c9bd57p-1, -0x1.271a5001975b3p-1, 0x1.a9544c9bc6f69p-3,
+       -0x1.2b04fcf02cfe4p-5, 0x1.52f61b5bf2013p-9},
+      {0x1.7c8089dfc728fp0, -0x1.4aca78f669032p1, 0x1.a50b5166895f1p0,
+       -0x1.52bccda96d2ddp-2, -0x1.db1a7723e7021p-4, 0x1.3e221766f1413p-4,
+       -0x1.07c689c1529d4p-6, 0x1.3e28897c72f86p-10},
+      {0x1.c677ecd59f0c9p0, -0x1.bddee5876d025p1, 0x1.6c05470bf9855p1,
+       -0x1.3838ea389805p0, 0x1.1e0da063ba588p-2, -0x1.c8701de42abb8p-6,
+       -0x1.d505f665736f4p-12, 0x1.ca3e25ddd2f8ep-13},
+      {0x1.0346dd8ccf496p1, -0x1.0e36567843072p2, 0x1.e39ee1779480ap1,
+       -0x1.e05781af98971p0, 0x1.1cd860f207bc6p-1, -0x1.91471b20d7ea7p-4,
+       0x1.3472b380bd896p-7, -0x1.8a32a1c029365p-12},
+      {0x1.175cf188a8137p1, -0x1.2a6aea014a1a7p2, 0x1.13c3894652ae3p2,
+       -0x1.1d97a2368998p1, 0x1.65c57ae4f9a2ep-1, -0x1.0ee6b7e8303e8p-3,
+       0x1.cae7deaf84ca3p-7, -0x1.4f32ea2cd38e3p-11},
+      {0x1.1b0bf6d5ea90bp1, -0x1.2f6bcdbfd2bf8p2, 0x1.199682b413d76p2,
+       -0x1.251f9f5a8b0dap1, 0x1.7174a7ecafc0ep-1, -0x1.19c6a1d04557fp-3,
+       0x1.e164b9a559bfdp-7, -0x1.6320142d50654p-11},
+      {0x1.0d8865040d536p1, -0x1.1e4d7b5aadcb3p2, 0x1.06ffcd830db2p2,
+       -0x1.0eb1633113f1fp1, 0x1.50f8dac72a8f9p-1, -0x1.fb183d2925a88p-4,
+       0x1.aadfbeb9cff03p-7, -0x1.35fe3091fd72bp-11},
+      {0x1.e30746fa5c05ap0, -0x1.f8814cab9a532p1, 0x1.c70d7871e1e66p1,
+       -0x1.cb40d988d46f7p0, 0x1.17ea520e30ee5p-1, -0x1.9bf4656539b33p-4,
+       0x1.52ba88a712da3p-7, -0x1.dff8c68511d54p-12},
+      {0x1.980594d76c9c6p0, -0x1.a1111fecc5d33p1, 0x1.6fac90467734ap1,
+       -0x1.6a38a8cadbc1dp0, 0x1.ae83820056a13p-2, -0x1.3489645378924p-4,
+       0x1.eda542162eab4p-8, -0x1.53f0f5d191e15p-12},
+      {0x1.4593f0667381p0, -0x1.44c18f230e828p1, 0x1.17126063fa673p1,
+       -0x1.0bb8315d99e44p0, 0x1.358c4f8bf482ep-2, -0x1.af3eebe9cb455p-5,
+       0x1.4f090acf146ep-8, -0x1.bfc7954478304p-13},
+      {0x1.ebcae3fdca674p-1, -0x1.dde1bcf5a5146p0, 0x1.8fb142f724b27p0,
+       -0x1.74e2bafe8789fp-1, 0x1.a2f67a40275d7p-3, -0x1.1b6406e10fca4p-5,
+       0x1.ab4ed0d6da84fp-9, -0x1.14f03579c96fep-13},
+      {0x1.6038027133b66p-1, -0x1.4d18991ed6939p0, 0x1.0ef1d6fa80951p0,
+       -0x1.eb55a8112702cp-2, 0x1.0c19cb48e5a46p-3, -0x1.60151a50d69f3p-6,
+       0x1.018b2a2cf7aa6p-9, -0x1.43bda617ae0e6p-14},
+      {0x1.df3b0de232601p-2, -0x1.b8e05100448c4p-1, 0x1.5ca6e2528e8d1p-1,
+       -0x1.332de46c2acb5p-2, 0x1.4595849385e42p-4, -0x1.9f152046c7178p-7,
+       0x1.26a2e0a31e9fp-10, -0x1.67436e13d44ddp-15},
+      {0x1.3628cccb902e9p-2, -0x1.1587e442957f9p-1, 0x1.aabedffc2818p-2,
+       -0x1.6d5f4f24a94a1p-3, 0x1.782f957586c48p-5, -0x1.d1b4c8cd3d9ap-8,
+       0x1.40e4007f68b4dp-11, -0x1.7bb3eac541f35p-16},
+      {0x1.7e774f23dd5d8p-3, -0x1.4ce552c93ab17p-2, 0x1.f1ba61425c57ap-3,
+       -0x1.9e391990469f7p-4, 0x1.9e6adf17223b9p-6, -0x1.f2601fc8d5f7ep-9,
+       0x1.4d7f6c4dab8cap-12, -0x1.7f24e1a9c9b8ap-17},
+      {0x1.c0bbabb6ec859p-4, -0x1.7bf5902ff3dfcp-3, 0x1.143e24f2802a6p-3,
+       -0x1.bf08869d7e41p-5, 0x1.b2b78fe704f97p-7, -0x1.fc05ca0a46accp-10,
+       0x1.4a472fc484f8ap-13, -0x1.7091cb146f95ap-18},
+  };
+
+  using FPBits = typename fputil::FPBits<float16>;
+  FPBits xbits(x);
+  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() ? static_cast<float16>(2.0)
+                          : static_cast<float16>(0.0);
+  }
+
+  if (LIBC_UNLIKELY(x_abs == 0))
+    return fputil::cast<float16>(1.0);
+
+  // 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.
+      double xd = static_cast<double>(x);
+      return static_cast<float16>(2.0 - 0x1.0p-12 * (4.0 / -xd));
+    }
+    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.
+
+  float xf = static_cast<float>(xbits.abs().get_val());
+  int idx = static_cast<int>(xf * 8.0f);
+
+  double xd = static_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_result = fputil::multiply_add(x4, p47, p03);
+
+  if (xbits.is_neg())
+    return static_cast<float16>(2.0 - erfc_abs_result);
+
+  return static_cast<float16>(erfc_abs_result);
+}
+
+} // 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 e37e22fdb58e6..233a1afec55ef 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -136,6 +136,8 @@ add_math_entrypoint_object(dsubf128)
 add_math_entrypoint_object(erf)
 add_math_entrypoint_object(erff)
 
+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 1a3588a71219b..b9b921fb374a4 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -1358,6 +1358,17 @@ add_entrypoint_object(
     libc.src.errno.errno
 )
 
+add_entrypoint_object(
+  erfcf16
+  SRCS
+    erfcf16.cpp
+  HDRS
+    ../erfcf16.h
+  DEPENDS
+    libc.src.__support.math.erfcf16
+    libc.src.errno.errno
+)
+
 add_entrypoint_object(
   exp
   SRCS
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/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index ff5c511922171..363ae42ed0a1e 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -2625,6 +2625,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 5afd3a9f22967..8f0f6703fceaf 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -5073,6 +5073,16 @@ add_fp_unittest(
     libc.src.__support.FPUtil.fp_bits
 )
 
+add_fp_unittest(
+  erfcf16_test
+  SUITE
+    libc-math-smoke-tests
+  SRCS
+    erfcf16_test.cpp
+  DEPENDS
+    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..f6e11da360f4e
--- /dev/null
+++ b/libc/test/src/math/smoke/erfcf16_test.cpp
@@ -0,0 +1,51 @@
+//===-- 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/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));
+}
+
+#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 6b78bee6e7cae..9725344d77930 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 9f4107a7961d2..c7a2a71436a2e 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 a7d307b47c3e8..5f9b3b5b1d80c 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 3a8f5343b3118..67bc787561e77 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,



More information about the libc-commits mailing list