[libc-commits] [libc] [libc][math] Implement C23 half precision erfc function (PR #180930)
via libc-commits
libc-commits at lists.llvm.org
Sun Mar 15 21:06:07 PDT 2026
https://github.com/AnonMiraj updated https://github.com/llvm/llvm-project/pull/180930
>From 6f952a3e1fb42c66fccc9223409e5c02ef6dea1e 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 1/5] [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 | 7 +
libc/shared/math.h | 1 +
libc/shared/math/erfcf16.h | 27 +++
libc/src/__support/math/CMakeLists.txt | 12 ++
libc/src/__support/math/erfcf16.h | 196 ++++++++++++++++++++++
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/shared/CMakeLists.txt | 1 +
libc/test/shared/shared_math_test.cpp | 1 +
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 +
26 files changed, 427 insertions(+), 1 deletion(-)
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 98c27d300ad9d..1f9c8eeabdadc 100644
--- a/libc/config/gpu/amdgpu/entrypoints.txt
+++ b/libc/config/gpu/amdgpu/entrypoints.txt
@@ -314,6 +314,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 532fcda9ab3be..e5c2c96917403 100644
--- a/libc/config/gpu/nvptx/entrypoints.txt
+++ b/libc/config/gpu/nvptx/entrypoints.txt
@@ -314,6 +314,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 380c91e1ac396..facdfa9412e58 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -468,6 +468,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 8611082f87003..ac6f841e094a9 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -286,6 +286,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 ddb24c3eea178..d7048c286c045 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -472,6 +472,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 9102de9bf0417..cc924088a39b1 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -517,6 +517,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 02b56ab01081b..ed1f02f92ec94 100644
--- a/libc/config/windows/entrypoints.txt
+++ b/libc/config/windows/entrypoints.txt
@@ -168,6 +168,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 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 61c3ddffd74e5..07a836bb066c4 100644
--- a/libc/shared/math.h
+++ b/libc/shared/math.h
@@ -75,6 +75,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 8bb935d863a9b..4c04d1af9e23d 100644
--- a/libc/src/__support/math/CMakeLists.txt
+++ b/libc/src/__support/math/CMakeLists.txt
@@ -884,6 +884,18 @@ add_header_library(
libc.src.__support.macros.properties.cpu_features
)
+add_header_library(
+ erfcf16
+ HDRS
+ erfcf16.h
+ DEPENDS
+ 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..a1e39521a8483
--- /dev/null
+++ b/libc/src/__support/math/erfcf16.h
@@ -0,0 +1,196 @@
+//===-- 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/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 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.
+ 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 = fputil::multiply_add(x4, p47, p03);
+
+ if (xbits.is_neg())
+ return static_cast<float16>(2.0 - erfc_abs);
+
+ return static_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 98dba11f34c86..235025b7e1717 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -141,6 +141,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 07c48c9a04c98..3bd7ddf58c177 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -1293,7 +1293,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 2e351d8051bed..778f626293227 100644
--- a/libc/test/shared/CMakeLists.txt
+++ b/libc/test/shared/CMakeLists.txt
@@ -74,6 +74,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 4eb950f030a1a..fa302b5f96dd5 100644
--- a/libc/test/shared/shared_math_test.cpp
+++ b/libc/test/shared/shared_math_test.cpp
@@ -31,6 +31,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 2710f4cbdbe31..626dd280cf0f0 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -2711,6 +2711,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 d92e6b728b63e..7c56e188ba35e 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -5169,6 +5169,16 @@ add_fp_unittest(
libc.src.math.erff16
)
+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 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,
>From a471c8dfd386669d16c552b34a54e77627aedb12 Mon Sep 17 00:00:00 2001
From: Ezzeldin Ibrahim <ezzibrahimx at gmail.com>
Date: Sun, 15 Feb 2026 08:07:27 +0200
Subject: [PATCH 2/5] fix windows build fail
---
libc/config/windows/entrypoints.txt | 1 -
1 file changed, 1 deletion(-)
diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt
index ed1f02f92ec94..02b56ab01081b 100644
--- a/libc/config/windows/entrypoints.txt
+++ b/libc/config/windows/entrypoints.txt
@@ -168,7 +168,6 @@ 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
>From 6b373e0706828692e2968d9109672d93a03a6791 Mon Sep 17 00:00:00 2001
From: Ezzeldin Ibrahim <ezzibrahimx at gmail.com>
Date: Tue, 17 Feb 2026 18:19:52 +0200
Subject: [PATCH 3/5] guard float16 entrypoints
---
libc/config/gpu/amdgpu/entrypoints.txt | 2 +-
libc/config/gpu/nvptx/entrypoints.txt | 2 +-
libc/config/linux/aarch64/entrypoints.txt | 2 +-
libc/config/linux/arm/entrypoints.txt | 1 -
libc/config/linux/riscv/entrypoints.txt | 2 +-
libc/config/linux/x86_64/entrypoints.txt | 2 +-
6 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/libc/config/gpu/amdgpu/entrypoints.txt b/libc/config/gpu/amdgpu/entrypoints.txt
index 1f9c8eeabdadc..b6a792c588cf5 100644
--- a/libc/config/gpu/amdgpu/entrypoints.txt
+++ b/libc/config/gpu/amdgpu/entrypoints.txt
@@ -314,7 +314,6 @@ 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
@@ -533,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 e5c2c96917403..e94ae4324f8f0 100644
--- a/libc/config/gpu/nvptx/entrypoints.txt
+++ b/libc/config/gpu/nvptx/entrypoints.txt
@@ -314,7 +314,6 @@ 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
@@ -535,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 facdfa9412e58..84ae96c68b7b1 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -468,7 +468,6 @@ 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
@@ -689,6 +688,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/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt
index ac6f841e094a9..8611082f87003 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -286,7 +286,6 @@ 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 d7048c286c045..2fd2b8df41e4b 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -472,7 +472,6 @@ 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
@@ -703,6 +702,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 cc924088a39b1..ef4daea525de4 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -517,7 +517,6 @@ 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
@@ -752,6 +751,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
>From e110eb0371900d308382c917198ce564462f7bb0 Mon Sep 17 00:00:00 2001
From: Anonmiraj <ezzibrahimx at gmail.com>
Date: Fri, 13 Mar 2026 06:14:31 +0200
Subject: [PATCH 4/5] fix nits
---
libc/src/__support/math/CMakeLists.txt | 1 +
libc/src/__support/math/erfcf16.h | 19 +++++++++----------
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt
index 4c04d1af9e23d..030fa1b4ace36 100644
--- a/libc/src/__support/math/CMakeLists.txt
+++ b/libc/src/__support/math/CMakeLists.txt
@@ -889,6 +889,7 @@ add_header_library(
HDRS
erfcf16.h
DEPENDS
+ libc.src.__support.FPUtil.cast
libc.src.__support.FPUtil.fenv_impl
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.multiply_add
diff --git a/libc/src/__support/math/erfcf16.h b/libc/src/__support/math/erfcf16.h
index a1e39521a8483..510017f7d1aea 100644
--- a/libc/src/__support/math/erfcf16.h
+++ b/libc/src/__support/math/erfcf16.h
@@ -15,6 +15,7 @@
#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
@@ -141,8 +142,8 @@ LIBC_INLINE float16 erfcf16(float16 x) {
return x;
}
// erfc(+Inf) = 0, erfc(-Inf) = 2
- return xbits.is_neg() ? static_cast<float16>(2.0)
- : static_cast<float16>(0.0);
+ return xbits.is_neg() ? fputil::cast<float16>(2.0)
+ : fputil::cast<float16>(0.0);
}
if (LIBC_UNLIKELY(x_abs == 0))
@@ -153,8 +154,8 @@ LIBC_INLINE float16 erfcf16(float16 x) {
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));
+ double xd = fputil::cast<double>(x);
+ return fputil::cast<float16>(2.0 - 0x1.0p-12 * (4.0 / -xd));
}
return 0.0f16;
}
@@ -164,10 +165,8 @@ LIBC_INLINE float16 erfcf16(float16 x) {
// 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());
+ int idx = static_cast<int>(xbits.abs().get_val() * 8.0f16);
+ double xd = fputil::cast<double>(xbits.abs().get_val());
double xsq = xd * xd;
double x4 = xsq * xsq;
@@ -182,9 +181,9 @@ LIBC_INLINE float16 erfcf16(float16 x) {
double erfc_abs = fputil::multiply_add(x4, p47, p03);
if (xbits.is_neg())
- return static_cast<float16>(2.0 - erfc_abs);
+ return fputil::cast<float16>(2.0 - erfc_abs);
- return static_cast<float16>(erfc_abs);
+ return fputil::cast<float16>(erfc_abs);
}
} // namespace math
>From 153b86833feb17e13df6cf8c521e28fa361a5a29 Mon Sep 17 00:00:00 2001
From: Anonmiraj <ezzibrahimx at gmail.com>
Date: Mon, 16 Mar 2026 05:48:23 +0200
Subject: [PATCH 5/5] fix underflow excpetion
---
libc/src/__support/math/erfcf16.h | 4 ++++
libc/test/src/math/smoke/CMakeLists.txt | 1 +
libc/test/src/math/smoke/erfcf16_test.cpp | 15 +++++++++++++++
3 files changed, 20 insertions(+)
diff --git a/libc/src/__support/math/erfcf16.h b/libc/src/__support/math/erfcf16.h
index 510017f7d1aea..2306e4981d572 100644
--- a/libc/src/__support/math/erfcf16.h
+++ b/libc/src/__support/math/erfcf16.h
@@ -157,6 +157,10 @@ LIBC_INLINE float16 erfcf16(float16 x) {
double xd = fputil::cast<double>(x);
return fputil::cast<float16>(2.0 - 0x1.0p-12 * (4.0 / -xd));
}
+ 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;
}
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index 7c56e188ba35e..2e001ee848313 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -5176,6 +5176,7 @@ add_fp_unittest(
SRCS
erfcf16_test.cpp
DEPENDS
+ libc.hdr.errno_macros
libc.src.math.erfcf16
)
diff --git a/libc/test/src/math/smoke/erfcf16_test.cpp b/libc/test/src/math/smoke/erfcf16_test.cpp
index f6e11da360f4e..22df1bcbb0d29 100644
--- a/libc/test/src/math/smoke/erfcf16_test.cpp
+++ b/libc/test/src/math/smoke/erfcf16_test.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+#include "hdr/errno_macros.h"
#include "hdr/math_macros.h"
#include "hdr/stdint_proxy.h"
#include "src/math/erfcf16.h"
@@ -25,6 +26,20 @@ TEST_F(LlvmLibcErfcTest, SpecialNumbers) {
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;
More information about the libc-commits
mailing list