[libc-commits] [libc] [llvm] [libc][math] Implement C23 half precision erf function (PR #179251)

via libc-commits libc-commits at lists.llvm.org
Wed Mar 11 19:08:35 PDT 2026


https://github.com/AnonMiraj updated https://github.com/llvm/llvm-project/pull/179251

>From fd3cd2a42c844452aeec79054ff4ca6bdb42a846 Mon Sep 17 00:00:00 2001
From: Ezzeldin Ibrahim <ezzibrahimx at gmail.com>
Date: Mon, 2 Feb 2026 15:00:56 +0200
Subject: [PATCH 1/6] [libc][math] Implement C23 half precision erf 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/erff16.h                     |  27 ++++
 libc/src/__support/math/CMakeLists.txt        |  13 ++
 libc/src/__support/math/common_constants.h    | 104 +++++++++++++++
 libc/src/__support/math/erff.h                | 120 ++----------------
 libc/src/__support/math/erff16.h              |  90 +++++++++++++
 libc/src/math/CMakeLists.txt                  |   1 +
 libc/src/math/erff16.h                        |  21 +++
 libc/src/math/generic/CMakeLists.txt          |  11 ++
 libc/src/math/generic/erff16.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/erff16_test.cpp            |  43 +++++++
 libc/test/src/math/smoke/CMakeLists.txt       |  10 ++
 libc/test/src/math/smoke/erff16_test.cpp      |  51 ++++++++
 .../llvm-project-overlay/libc/BUILD.bazel     |  20 +++
 25 files changed, 446 insertions(+), 109 deletions(-)
 create mode 100644 libc/shared/math/erff16.h
 create mode 100644 libc/src/__support/math/erff16.h
 create mode 100644 libc/src/math/erff16.h
 create mode 100644 libc/src/math/generic/erff16.cpp
 create mode 100644 libc/test/src/math/erff16_test.cpp
 create mode 100644 libc/test/src/math/smoke/erff16_test.cpp

diff --git a/libc/config/gpu/amdgpu/entrypoints.txt b/libc/config/gpu/amdgpu/entrypoints.txt
index 0441207ace96b..38a6aae44f55f 100644
--- a/libc/config/gpu/amdgpu/entrypoints.txt
+++ b/libc/config/gpu/amdgpu/entrypoints.txt
@@ -315,6 +315,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.dmull
     libc.src.math.dsqrtl
     libc.src.math.erff
+    libc.src.math.erff16
     libc.src.math.exp
     libc.src.math.exp10
     libc.src.math.exp10f
diff --git a/libc/config/gpu/nvptx/entrypoints.txt b/libc/config/gpu/nvptx/entrypoints.txt
index f127ba6358b43..1ee0f01a4e3b4 100644
--- a/libc/config/gpu/nvptx/entrypoints.txt
+++ b/libc/config/gpu/nvptx/entrypoints.txt
@@ -315,6 +315,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.dmull
     libc.src.math.dsqrtl
     libc.src.math.erff
+    libc.src.math.erff16
     libc.src.math.exp
     libc.src.math.exp10
     libc.src.math.exp10f
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 658015cbefae9..7aa3c28901672 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -463,6 +463,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.dsqrtl
     libc.src.math.dsubl
     libc.src.math.erff
+    libc.src.math.erff16
     libc.src.math.exp
     libc.src.math.exp10
     libc.src.math.exp10f
diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt
index 92ffe7e8d0bd5..6284136c47396 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -285,6 +285,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.dfmal
     libc.src.math.dsqrtl
     libc.src.math.erff
+    libc.src.math.erff16
     libc.src.math.exp
     libc.src.math.exp10
     libc.src.math.exp10f
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 689f7384776d5..dff2d3ea25ec4 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -467,6 +467,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.dsqrtl
     libc.src.math.dsubl
     libc.src.math.erff
+    libc.src.math.erff16
     libc.src.math.exp
     libc.src.math.exp10
     libc.src.math.exp10f
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 939b64797b7f5..51ba34d9034ad 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -512,6 +512,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.ddivl
     libc.src.math.dsubl
     libc.src.math.erff
+    libc.src.math.erff16
     libc.src.math.exp
     libc.src.math.exp10
     libc.src.math.exp10f
diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt
index 28608e8c327d1..d5a8026169a33 100644
--- a/libc/config/windows/entrypoints.txt
+++ b/libc/config/windows/entrypoints.txt
@@ -166,6 +166,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.dfmal
     libc.src.math.dsubl
     libc.src.math.erff
+    libc.src.math.erff16
     libc.src.math.exp
     libc.src.math.expf
     libc.src.math.exp10
diff --git a/libc/include/math.yaml b/libc/include/math.yaml
index afd3ae33305c1..8c333a2064379 100644
--- a/libc/include/math.yaml
+++ b/libc/include/math.yaml
@@ -396,6 +396,12 @@ functions:
     return_type: float
     arguments:
       - type: float
+  - name: erff16
+    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 ede0ebd5371ac..36287b339cb14 100644
--- a/libc/shared/math.h
+++ b/libc/shared/math.h
@@ -74,6 +74,7 @@
 #include "math/dfmal.h"
 #include "math/dsqrtl.h"
 #include "math/erff.h"
+#include "math/erff16.h"
 #include "math/exp.h"
 #include "math/exp10.h"
 #include "math/exp10f.h"
diff --git a/libc/shared/math/erff16.h b/libc/shared/math/erff16.h
new file mode 100644
index 0000000000000..9e9f3f5c8e13e
--- /dev/null
+++ b/libc/shared/math/erff16.h
@@ -0,0 +1,27 @@
+//===-- Shared erff16 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_ERFF16_H
+#define LLVM_LIBC_SHARED_MATH_ERFF16_H
+
+#ifdef LIBC_TYPES_HAS_FLOAT16
+
+#include "shared/libc_common.h"
+#include "src/__support/math/erff16.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace shared {
+
+using math::erff16;
+
+} // namespace shared
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LIBC_TYPES_HAS_FLOAT16
+
+#endif // LLVM_LIBC_SHARED_MATH_ERFF16_H
diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt
index 315cc1069c1d2..a7527a6d4cb06 100644
--- a/libc/src/__support/math/CMakeLists.txt
+++ b/libc/src/__support/math/CMakeLists.txt
@@ -863,6 +863,19 @@ add_header_library(
   erff
   HDRS
     erff.h
+  DEPENDS
+    .common_constants
+    libc.src.__support.FPUtil.fp_bits
+    libc.src.__support.FPUtil.except_value_utils
+    libc.src.__support.FPUtil.multiply_add
+    libc.src.__support.FPUtil.polyeval
+    libc.src.__support.macros.optimization
+)
+
+add_header_library(
+  erff16
+  HDRS
+    erff16.h
   DEPENDS
     libc.src.__support.FPUtil.fp_bits
     libc.src.__support.FPUtil.except_value_utils
diff --git a/libc/src/__support/math/common_constants.h b/libc/src/__support/math/common_constants.h
index 4548cd506bb99..ad1be400fcb86 100644
--- a/libc/src/__support/math/common_constants.h
+++ b/libc/src/__support/math/common_constants.h
@@ -574,6 +574,110 @@ LIBC_INLINE_VAR constexpr double EXP_M2[128] = {
     0x1.568bb722dd593p1, 0x1.593b7d72305bbp1,
 };
 
+// Polynomials approximating erf(x)/x on ( k/8, (k + 1)/8 ) generated by
+// Sollya with: > P = fpminimax(erf(x)/x, [|0, 2, 4, 6, 8, 10, 12, 14|],
+// [|D...|],
+//                 [k/8, (k + 1)/8]);
+// for k = 0..31.
+constexpr double ERFF_COEFFS[32][8] = {
+    {0x1.20dd750429b6dp0, -0x1.812746b037753p-2, 0x1.ce2f219e8596ap-4,
+     -0x1.b82cdacb78fdap-6, 0x1.56479297dfda5p-8, -0x1.8b3ac5455ef02p-11,
+     -0x1.126fcac367e3bp-8, 0x1.2d0bdb3ba4984p-4},
+    {0x1.20dd750429b6dp0, -0x1.812746b0379a8p-2, 0x1.ce2f21a03cf2ap-4,
+     -0x1.b82ce30de083ep-6, 0x1.565bcad3eb60fp-8, -0x1.c02c66f659256p-11,
+     0x1.f92f673385229p-14, -0x1.def402648ae9p-17},
+    {0x1.20dd750429b34p0, -0x1.812746b032dcep-2, 0x1.ce2f219d84aaep-4,
+     -0x1.b82ce22dcf139p-6, 0x1.565b9efcd4af1p-8, -0x1.c021f1af414bcp-11,
+     0x1.f7c6d177eff82p-14, -0x1.c9e4410dcf865p-17},
+    {0x1.20dd750426eabp0, -0x1.812746ae592c7p-2, 0x1.ce2f211525f14p-4,
+     -0x1.b82ccc125e63fp-6, 0x1.56596f261cfd3p-8, -0x1.bfde1ff8eeecfp-11,
+     0x1.f31a9d15dc5d8p-14, -0x1.a5a4362844b3cp-17},
+    {0x1.20dd75039c705p0, -0x1.812746777e74dp-2, 0x1.ce2f17af98a1bp-4,
+     -0x1.b82be4b817cbep-6, 0x1.564bec2e2962ep-8, -0x1.bee86f9da3558p-11,
+     0x1.e9443689dc0ccp-14, -0x1.79c0f230805d8p-17},
+    {0x1.20dd74f811211p0, -0x1.81274371a3e8fp-2, 0x1.ce2ec038262e5p-4,
+     -0x1.b8265b82c5e1fp-6, 0x1.5615a2e239267p-8, -0x1.bc63ae023dcebp-11,
+     0x1.d87c2102f7e06p-14, -0x1.49584bea41d62p-17},
+    {0x1.20dd746d063e3p0, -0x1.812729a8a950fp-2, 0x1.ce2cb0a2df232p-4,
+     -0x1.b80eca1f51278p-6, 0x1.5572e26c46815p-8, -0x1.b715e5638b65ep-11,
+     0x1.bfbb195484968p-14, -0x1.177a565c15c52p-17},
+    {0x1.20dd701b44486p0, -0x1.812691145f237p-2, 0x1.ce23a06b8cfd9p-4,
+     -0x1.b7c1dc7245288p-6, 0x1.53e92f7f397ddp-8, -0x1.ad97cc4acf0b2p-11,
+     0x1.9f028b2b09b71p-14, -0x1.cdc4da08da8c1p-18},
+    {0x1.20dd5715ac332p0, -0x1.8123e680bd0ebp-2, 0x1.ce0457aded691p-4,
+     -0x1.b6f52d52bed4p-6, 0x1.50c291b84414cp-8, -0x1.9ea246b1ad4a9p-11,
+     0x1.77654674e0cap-14, -0x1.737c11a1bcebbp-18},
+    {0x1.20dce6593e114p0, -0x1.811a59c02eadcp-2, 0x1.cdab53c7cd7d5p-4,
+     -0x1.b526d2e321eedp-6, 0x1.4b1d32cd8b994p-8, -0x1.8963143ec0a1ep-11,
+     0x1.4ad5700e4db91p-14, -0x1.231e100e43ef2p-18},
+    {0x1.20db48bfd5a62p0, -0x1.80fdd84f9e308p-2, 0x1.ccd340d462983p-4,
+     -0x1.b196a2928768p-6, 0x1.4210c2c13a0f7p-8, -0x1.6dbdfb4ff71aep-11,
+     0x1.1bca2d17fbd71p-14, -0x1.bca36f90c7cf5p-19},
+    {0x1.20d64b2f8f508p0, -0x1.80b4d4f19fa8bp-2, 0x1.cb088197262e3p-4,
+     -0x1.ab51fd02e5b99p-6, 0x1.34e1e5e81a632p-8, -0x1.4c66377b502cep-11,
+     0x1.d9ad25066213cp-15, -0x1.4b0df7dd0cfa1p-19},
+    {0x1.20c8fc1243576p0, -0x1.8010cb2009e27p-2, 0x1.c7a47e9299315p-4,
+     -0x1.a155be5683654p-6, 0x1.233502694997bp-8, -0x1.26c94b7d813p-11,
+     0x1.8094f1de25fb9p-15, -0x1.e0e3d776c6eefp-20},
+    {0x1.20a9bd1611bc1p0, -0x1.7ec7fbce83f9p-2, 0x1.c1d757d7317b7p-4,
+     -0x1.92c160cd589fp-6, 0x1.0d307269cc5c2p-8, -0x1.fda5b0d2d1879p-12,
+     0x1.2fdd7b3b14a7fp-15, -0x1.54eed4a26af5ap-20},
+    {0x1.20682834f943dp0, -0x1.7c73f747bf5a9p-2, 0x1.b8c2db4a9ffd1p-4,
+     -0x1.7f0e4ffe989ecp-6, 0x1.e7061eae4166ep-9, -0x1.ad36e873fff2dp-12,
+     0x1.d39222396128ep-16, -0x1.d83dacec5ea6bp-21},
+    {0x1.1feb8d12676d7p0, -0x1.7898347284afep-2, 0x1.aba3466b34451p-4,
+     -0x1.663adc573e2f9p-6, 0x1.ae99fb17c3e08p-9, -0x1.602f950ad5535p-12,
+     0x1.5e9717490609dp-16, -0x1.3fca107bbc8d5p-21},
+    {0x1.1f12fe3c536fap0, -0x1.72b1d1f22e6d3p-2, 0x1.99fc0eed4a896p-4,
+     -0x1.48db0a87bd8c6p-6, 0x1.73e368895aa61p-9, -0x1.19b35d5301fc8p-12,
+     0x1.007987e4bb033p-16, -0x1.a7edcd4c2dc7p-22},
+    {0x1.1db7b0df84d5dp0, -0x1.6a4e4a41cde02p-2, 0x1.83bbded16455dp-4,
+     -0x1.2809b3b36977ep-6, 0x1.39c08bab44679p-9, -0x1.b7b45a70ed119p-13,
+     0x1.6e99b36410e7bp-17, -0x1.13619bb7ebc0cp-22},
+    {0x1.1bb1c85c4a527p0, -0x1.5f23b99a249a3p-2, 0x1.694c91fa0d12cp-4,
+     -0x1.053e1ce11c72dp-6, 0x1.02bf72c50ea78p-9, -0x1.4f478fb56cb02p-13,
+     0x1.005f80ecbe213p-17, -0x1.5f2446bde7f5bp-23},
+    {0x1.18dec3bd51f9dp0, -0x1.5123f58346186p-2, 0x1.4b8a1ca536ab4p-4,
+     -0x1.c4243015cc723p-7, 0x1.a1a8a01d351efp-10, -0x1.f466b34f1d86bp-14,
+     0x1.5f835eea0bf6ap-18, -0x1.b83165b939234p-24},
+    {0x1.152804c3369f4p0, -0x1.4084cd4afd4bcp-2, 0x1.2ba2e836e47aap-4,
+     -0x1.800f2dfc6904bp-7, 0x1.4a6daf0669c59p-10, -0x1.6e326ab872317p-14,
+     0x1.d9761a6a755a5p-19, -0x1.0fca33f9dd4b5p-24},
+    {0x1.1087ad68356aap0, -0x1.2dbb044707459p-2, 0x1.0aea8ceaa0384p-4,
+     -0x1.40b516d52b3d2p-7, 0x1.00c9e05f01d22p-10, -0x1.076afb0dc0ff7p-14,
+     0x1.39fadec400657p-19, -0x1.4b5761352e7e3p-25},
+    {0x1.0b0a7a8ba4a22p0, -0x1.196990d22d4a1p-2, 0x1.d5551e6ac0c4dp-5,
+     -0x1.07cce1770bd1ap-7, 0x1.890347b8848bfp-11, -0x1.757ec96750b6ap-15,
+     0x1.9b258a1e06bcep-20, -0x1.8fc6d22da7572p-26},
+    {0x1.04ce2be70fb47p0, -0x1.0449e4b0b9cacp-2, 0x1.97f7424f4b0e7p-5,
+     -0x1.ac825439c42f4p-8, 0x1.28f5f65426dfbp-11, -0x1.05b699a90f90fp-15,
+     0x1.0a888eecf4593p-20, -0x1.deace2b32bb31p-27},
+    {0x1.fbf9fb0e11cc8p-1, -0x1.de2640856545ap-3, 0x1.5f5b1f47f851p-5,
+     -0x1.588bc71eb41b9p-8, 0x1.bc6a0a772f56dp-12, -0x1.6b9fad1f1657ap-16,
+     0x1.573204ba66504p-21, -0x1.1d38065c94e44p-27},
+    {0x1.ed8f18c99e031p-1, -0x1.b4cb6acd903b4p-3, 0x1.2c7f3dddd6fc1p-5,
+     -0x1.13052067df4ep-8, 0x1.4a5027444082fp-12, -0x1.f672bab0e2554p-17,
+     0x1.b83c756348cc9p-22, -0x1.534f1a1079499p-28},
+    {0x1.debd33044166dp-1, -0x1.8d7cd9053f7d8p-3, 0x1.ff9957fb3d6e7p-6,
+     -0x1.b50be55de0f36p-9, 0x1.e92c8ec53a628p-13, -0x1.5a4b88d508007p-17,
+     0x1.1a27737559e26p-22, -0x1.942ae62cb2c14p-29},
+    {0x1.cfdbf0386f3bdp-1, -0x1.68e33d93b0dc4p-3, 0x1.b2683d58f53dep-6,
+     -0x1.5a9174e70d26fp-9, 0x1.69ddd326d49cdp-13, -0x1.dd8f397a8219cp-18,
+     0x1.6a755016ad4ddp-23, -0x1.e366e0139187dp-30},
+    {0x1.c132adb8d7464p-1, -0x1.475a899f61b46p-3, 0x1.70a431397a77cp-6,
+     -0x1.12e3d35beeee2p-9, 0x1.0c16b05738333p-13, -0x1.4a47f873e144ep-18,
+     0x1.d3d494c698c02p-24, -0x1.2302c59547fe5p-30},
+    {0x1.b2f5fd05555e7p-1, -0x1.28feefbe03ec7p-3, 0x1.3923acbb3a676p-6,
+     -0x1.b4ff793cd6358p-10, 0x1.8ea0eb8c913bcp-14, -0x1.cb31ec2baceb1p-19,
+     0x1.30011e7e80c04p-24, -0x1.617710635cb1dp-31},
+    {0x1.a54853cd9593ep-1, -0x1.0dbdbaea4dc8ep-3, 0x1.0a93e2c20a0fdp-6,
+     -0x1.5c969ff401ea8p-10, 0x1.29e0cc64fe627p-14, -0x1.4160d8e9d3c2ap-19,
+     0x1.8e7b67594624ap-25, -0x1.b1cf2c975b09bp-32},
+    {0x1.983ceece09ff8p-1, -0x1.eacc78f7a2dp-4, 0x1.c74418410655fp-7,
+     -0x1.1756a050e441ep-10, 0x1.bff3650f7f548p-15, -0x1.c56c0217d3adap-20,
+     0x1.07b4918d0b489p-25, -0x1.0d4be8c1c50f8p-32},
+};
+
 } // namespace common_constants_internal
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/__support/math/erff.h b/libc/src/__support/math/erff.h
index a40b927c60a26..339bb8089543b 100644
--- a/libc/src/__support/math/erff.h
+++ b/libc/src/__support/math/erff.h
@@ -9,6 +9,7 @@
 #ifndef LLVM_LIBC_SRC___SUPPORT_MATH_ERFF_H
 #define LLVM_LIBC_SRC___SUPPORT_MATH_ERFF_H
 
+#include "common_constants.h" // ERFF_COEFFS
 #include "src/__support/FPUtil/FPBits.h"
 #include "src/__support/FPUtil/except_value_utils.h"
 #include "src/__support/FPUtil/multiply_add.h"
@@ -19,111 +20,8 @@ namespace LIBC_NAMESPACE_DECL {
 
 namespace math {
 
-LIBC_INLINE constexpr float erff(float x) {
-
-  // Polynomials approximating erf(x)/x on ( k/8, (k + 1)/8 ) generated by
-  // Sollya with: > P = fpminimax(erf(x)/x, [|0, 2, 4, 6, 8, 10, 12, 14|],
-  // [|D...|],
-  //                 [k/8, (k + 1)/8]);
-  // for k = 0..31.
-  constexpr double COEFFS[32][8] = {
-      {0x1.20dd750429b6dp0, -0x1.812746b037753p-2, 0x1.ce2f219e8596ap-4,
-       -0x1.b82cdacb78fdap-6, 0x1.56479297dfda5p-8, -0x1.8b3ac5455ef02p-11,
-       -0x1.126fcac367e3bp-8, 0x1.2d0bdb3ba4984p-4},
-      {0x1.20dd750429b6dp0, -0x1.812746b0379a8p-2, 0x1.ce2f21a03cf2ap-4,
-       -0x1.b82ce30de083ep-6, 0x1.565bcad3eb60fp-8, -0x1.c02c66f659256p-11,
-       0x1.f92f673385229p-14, -0x1.def402648ae9p-17},
-      {0x1.20dd750429b34p0, -0x1.812746b032dcep-2, 0x1.ce2f219d84aaep-4,
-       -0x1.b82ce22dcf139p-6, 0x1.565b9efcd4af1p-8, -0x1.c021f1af414bcp-11,
-       0x1.f7c6d177eff82p-14, -0x1.c9e4410dcf865p-17},
-      {0x1.20dd750426eabp0, -0x1.812746ae592c7p-2, 0x1.ce2f211525f14p-4,
-       -0x1.b82ccc125e63fp-6, 0x1.56596f261cfd3p-8, -0x1.bfde1ff8eeecfp-11,
-       0x1.f31a9d15dc5d8p-14, -0x1.a5a4362844b3cp-17},
-      {0x1.20dd75039c705p0, -0x1.812746777e74dp-2, 0x1.ce2f17af98a1bp-4,
-       -0x1.b82be4b817cbep-6, 0x1.564bec2e2962ep-8, -0x1.bee86f9da3558p-11,
-       0x1.e9443689dc0ccp-14, -0x1.79c0f230805d8p-17},
-      {0x1.20dd74f811211p0, -0x1.81274371a3e8fp-2, 0x1.ce2ec038262e5p-4,
-       -0x1.b8265b82c5e1fp-6, 0x1.5615a2e239267p-8, -0x1.bc63ae023dcebp-11,
-       0x1.d87c2102f7e06p-14, -0x1.49584bea41d62p-17},
-      {0x1.20dd746d063e3p0, -0x1.812729a8a950fp-2, 0x1.ce2cb0a2df232p-4,
-       -0x1.b80eca1f51278p-6, 0x1.5572e26c46815p-8, -0x1.b715e5638b65ep-11,
-       0x1.bfbb195484968p-14, -0x1.177a565c15c52p-17},
-      {0x1.20dd701b44486p0, -0x1.812691145f237p-2, 0x1.ce23a06b8cfd9p-4,
-       -0x1.b7c1dc7245288p-6, 0x1.53e92f7f397ddp-8, -0x1.ad97cc4acf0b2p-11,
-       0x1.9f028b2b09b71p-14, -0x1.cdc4da08da8c1p-18},
-      {0x1.20dd5715ac332p0, -0x1.8123e680bd0ebp-2, 0x1.ce0457aded691p-4,
-       -0x1.b6f52d52bed4p-6, 0x1.50c291b84414cp-8, -0x1.9ea246b1ad4a9p-11,
-       0x1.77654674e0cap-14, -0x1.737c11a1bcebbp-18},
-      {0x1.20dce6593e114p0, -0x1.811a59c02eadcp-2, 0x1.cdab53c7cd7d5p-4,
-       -0x1.b526d2e321eedp-6, 0x1.4b1d32cd8b994p-8, -0x1.8963143ec0a1ep-11,
-       0x1.4ad5700e4db91p-14, -0x1.231e100e43ef2p-18},
-      {0x1.20db48bfd5a62p0, -0x1.80fdd84f9e308p-2, 0x1.ccd340d462983p-4,
-       -0x1.b196a2928768p-6, 0x1.4210c2c13a0f7p-8, -0x1.6dbdfb4ff71aep-11,
-       0x1.1bca2d17fbd71p-14, -0x1.bca36f90c7cf5p-19},
-      {0x1.20d64b2f8f508p0, -0x1.80b4d4f19fa8bp-2, 0x1.cb088197262e3p-4,
-       -0x1.ab51fd02e5b99p-6, 0x1.34e1e5e81a632p-8, -0x1.4c66377b502cep-11,
-       0x1.d9ad25066213cp-15, -0x1.4b0df7dd0cfa1p-19},
-      {0x1.20c8fc1243576p0, -0x1.8010cb2009e27p-2, 0x1.c7a47e9299315p-4,
-       -0x1.a155be5683654p-6, 0x1.233502694997bp-8, -0x1.26c94b7d813p-11,
-       0x1.8094f1de25fb9p-15, -0x1.e0e3d776c6eefp-20},
-      {0x1.20a9bd1611bc1p0, -0x1.7ec7fbce83f9p-2, 0x1.c1d757d7317b7p-4,
-       -0x1.92c160cd589fp-6, 0x1.0d307269cc5c2p-8, -0x1.fda5b0d2d1879p-12,
-       0x1.2fdd7b3b14a7fp-15, -0x1.54eed4a26af5ap-20},
-      {0x1.20682834f943dp0, -0x1.7c73f747bf5a9p-2, 0x1.b8c2db4a9ffd1p-4,
-       -0x1.7f0e4ffe989ecp-6, 0x1.e7061eae4166ep-9, -0x1.ad36e873fff2dp-12,
-       0x1.d39222396128ep-16, -0x1.d83dacec5ea6bp-21},
-      {0x1.1feb8d12676d7p0, -0x1.7898347284afep-2, 0x1.aba3466b34451p-4,
-       -0x1.663adc573e2f9p-6, 0x1.ae99fb17c3e08p-9, -0x1.602f950ad5535p-12,
-       0x1.5e9717490609dp-16, -0x1.3fca107bbc8d5p-21},
-      {0x1.1f12fe3c536fap0, -0x1.72b1d1f22e6d3p-2, 0x1.99fc0eed4a896p-4,
-       -0x1.48db0a87bd8c6p-6, 0x1.73e368895aa61p-9, -0x1.19b35d5301fc8p-12,
-       0x1.007987e4bb033p-16, -0x1.a7edcd4c2dc7p-22},
-      {0x1.1db7b0df84d5dp0, -0x1.6a4e4a41cde02p-2, 0x1.83bbded16455dp-4,
-       -0x1.2809b3b36977ep-6, 0x1.39c08bab44679p-9, -0x1.b7b45a70ed119p-13,
-       0x1.6e99b36410e7bp-17, -0x1.13619bb7ebc0cp-22},
-      {0x1.1bb1c85c4a527p0, -0x1.5f23b99a249a3p-2, 0x1.694c91fa0d12cp-4,
-       -0x1.053e1ce11c72dp-6, 0x1.02bf72c50ea78p-9, -0x1.4f478fb56cb02p-13,
-       0x1.005f80ecbe213p-17, -0x1.5f2446bde7f5bp-23},
-      {0x1.18dec3bd51f9dp0, -0x1.5123f58346186p-2, 0x1.4b8a1ca536ab4p-4,
-       -0x1.c4243015cc723p-7, 0x1.a1a8a01d351efp-10, -0x1.f466b34f1d86bp-14,
-       0x1.5f835eea0bf6ap-18, -0x1.b83165b939234p-24},
-      {0x1.152804c3369f4p0, -0x1.4084cd4afd4bcp-2, 0x1.2ba2e836e47aap-4,
-       -0x1.800f2dfc6904bp-7, 0x1.4a6daf0669c59p-10, -0x1.6e326ab872317p-14,
-       0x1.d9761a6a755a5p-19, -0x1.0fca33f9dd4b5p-24},
-      {0x1.1087ad68356aap0, -0x1.2dbb044707459p-2, 0x1.0aea8ceaa0384p-4,
-       -0x1.40b516d52b3d2p-7, 0x1.00c9e05f01d22p-10, -0x1.076afb0dc0ff7p-14,
-       0x1.39fadec400657p-19, -0x1.4b5761352e7e3p-25},
-      {0x1.0b0a7a8ba4a22p0, -0x1.196990d22d4a1p-2, 0x1.d5551e6ac0c4dp-5,
-       -0x1.07cce1770bd1ap-7, 0x1.890347b8848bfp-11, -0x1.757ec96750b6ap-15,
-       0x1.9b258a1e06bcep-20, -0x1.8fc6d22da7572p-26},
-      {0x1.04ce2be70fb47p0, -0x1.0449e4b0b9cacp-2, 0x1.97f7424f4b0e7p-5,
-       -0x1.ac825439c42f4p-8, 0x1.28f5f65426dfbp-11, -0x1.05b699a90f90fp-15,
-       0x1.0a888eecf4593p-20, -0x1.deace2b32bb31p-27},
-      {0x1.fbf9fb0e11cc8p-1, -0x1.de2640856545ap-3, 0x1.5f5b1f47f851p-5,
-       -0x1.588bc71eb41b9p-8, 0x1.bc6a0a772f56dp-12, -0x1.6b9fad1f1657ap-16,
-       0x1.573204ba66504p-21, -0x1.1d38065c94e44p-27},
-      {0x1.ed8f18c99e031p-1, -0x1.b4cb6acd903b4p-3, 0x1.2c7f3dddd6fc1p-5,
-       -0x1.13052067df4ep-8, 0x1.4a5027444082fp-12, -0x1.f672bab0e2554p-17,
-       0x1.b83c756348cc9p-22, -0x1.534f1a1079499p-28},
-      {0x1.debd33044166dp-1, -0x1.8d7cd9053f7d8p-3, 0x1.ff9957fb3d6e7p-6,
-       -0x1.b50be55de0f36p-9, 0x1.e92c8ec53a628p-13, -0x1.5a4b88d508007p-17,
-       0x1.1a27737559e26p-22, -0x1.942ae62cb2c14p-29},
-      {0x1.cfdbf0386f3bdp-1, -0x1.68e33d93b0dc4p-3, 0x1.b2683d58f53dep-6,
-       -0x1.5a9174e70d26fp-9, 0x1.69ddd326d49cdp-13, -0x1.dd8f397a8219cp-18,
-       0x1.6a755016ad4ddp-23, -0x1.e366e0139187dp-30},
-      {0x1.c132adb8d7464p-1, -0x1.475a899f61b46p-3, 0x1.70a431397a77cp-6,
-       -0x1.12e3d35beeee2p-9, 0x1.0c16b05738333p-13, -0x1.4a47f873e144ep-18,
-       0x1.d3d494c698c02p-24, -0x1.2302c59547fe5p-30},
-      {0x1.b2f5fd05555e7p-1, -0x1.28feefbe03ec7p-3, 0x1.3923acbb3a676p-6,
-       -0x1.b4ff793cd6358p-10, 0x1.8ea0eb8c913bcp-14, -0x1.cb31ec2baceb1p-19,
-       0x1.30011e7e80c04p-24, -0x1.617710635cb1dp-31},
-      {0x1.a54853cd9593ep-1, -0x1.0dbdbaea4dc8ep-3, 0x1.0a93e2c20a0fdp-6,
-       -0x1.5c969ff401ea8p-10, 0x1.29e0cc64fe627p-14, -0x1.4160d8e9d3c2ap-19,
-       0x1.8e7b67594624ap-25, -0x1.b1cf2c975b09bp-32},
-      {0x1.983ceece09ff8p-1, -0x1.eacc78f7a2dp-4, 0x1.c74418410655fp-7,
-       -0x1.1756a050e441ep-10, 0x1.bff3650f7f548p-15, -0x1.c56c0217d3adap-20,
-       0x1.07b4918d0b489p-25, -0x1.0d4be8c1c50f8p-32},
-  };
+LIBC_INLINE static constexpr float erff(float x) {
+  using namespace common_constants_internal;
 
   using FPBits = typename fputil::FPBits<float>;
   FPBits xbits(x);
@@ -174,10 +72,14 @@ LIBC_INLINE constexpr float erff(float x) {
   int idx = static_cast<int>(FPBits(x_abs + EIGHT).get_val());
 
   double x4 = xsq * xsq;
-  double c0 = fputil::multiply_add(xsq, COEFFS[idx][1], COEFFS[idx][0]);
-  double c1 = fputil::multiply_add(xsq, COEFFS[idx][3], COEFFS[idx][2]);
-  double c2 = fputil::multiply_add(xsq, COEFFS[idx][5], COEFFS[idx][4]);
-  double c3 = fputil::multiply_add(xsq, COEFFS[idx][7], COEFFS[idx][6]);
+  double c0 =
+      fputil::multiply_add(xsq, ERFF_COEFFS[idx][1], ERFF_COEFFS[idx][0]);
+  double c1 =
+      fputil::multiply_add(xsq, ERFF_COEFFS[idx][3], ERFF_COEFFS[idx][2]);
+  double c2 =
+      fputil::multiply_add(xsq, ERFF_COEFFS[idx][5], ERFF_COEFFS[idx][4]);
+  double c3 =
+      fputil::multiply_add(xsq, ERFF_COEFFS[idx][7], ERFF_COEFFS[idx][6]);
 
   double x8 = x4 * x4;
   double p0 = fputil::multiply_add(x4, c1, c0);
diff --git a/libc/src/__support/math/erff16.h b/libc/src/__support/math/erff16.h
new file mode 100644
index 0000000000000..fa0cd97335cdf
--- /dev/null
+++ b/libc/src/__support/math/erff16.h
@@ -0,0 +1,90 @@
+//===-- Implementation header for erff16 ------------------------*- 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_ERFF16_H
+#define LLVM_LIBC_SRC___SUPPORT_MATH_ERFF16_H
+
+#include "include/llvm-libc-macros/float16-macros.h"
+
+#ifdef LIBC_TYPES_HAS_FLOAT16
+
+#include "common_constants.h" // ERFF_COEFFS
+#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 erff16(float16 x) {
+  using namespace common_constants_internal;
+
+  using FPBits = typename fputil::FPBits<float16>;
+  FPBits xbits(x);
+  uint16_t x_abs = xbits.abs().uintval();
+
+  // |x| >= 4.0
+  if (LIBC_UNLIKELY(x_abs >= 0x4400U)) {
+    // Check for NaN or 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;
+      }
+      // Inf -> returns 1.0 or -1.0
+      return xbits.is_neg() ? -1.0f16 : 1.0f16;
+    }
+
+    return static_cast<float16>(xbits.is_neg() ? -1.0 - x * 0x1.0p-50
+                                               : 1.0 - x * 0x1.0p-50);
+  }
+
+  // Polynomial approximation:
+  //   erf(x) ~ x * (c0 + c1 * x^2 + c2 * x^4 + ... + c7 * x^14)
+
+  using FPBits32 = typename fputil::FPBits<float>;
+  float xf = x;
+  FPBits32 xbits32(xf);
+  uint32_t x_abs32 = xbits32.abs().uintval();
+
+  constexpr uint32_t EIGHT = 3 << FPBits32::FRACTION_LEN;
+  int idx = static_cast<int>(FPBits32(x_abs32 + EIGHT).get_val());
+
+  double xd = static_cast<double>(x);
+  double xsq = xd * xd;
+
+  double x4 = xsq * xsq;
+  double c0 =
+      fputil::multiply_add(xsq, ERFF_COEFFS[idx][1], ERFF_COEFFS[idx][0]);
+  double c1 =
+      fputil::multiply_add(xsq, ERFF_COEFFS[idx][3], ERFF_COEFFS[idx][2]);
+  double c2 =
+      fputil::multiply_add(xsq, ERFF_COEFFS[idx][5], ERFF_COEFFS[idx][4]);
+  double c3 =
+      fputil::multiply_add(xsq, ERFF_COEFFS[idx][7], ERFF_COEFFS[idx][6]);
+
+  double x8 = x4 * x4;
+  double p0 = fputil::multiply_add(x4, c1, c0);
+  double p1 = fputil::multiply_add(x4, c3, c2);
+
+  return static_cast<float16>(xd * fputil::multiply_add(x8, p1, p0));
+}
+
+} // namespace math
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LIBC_TYPES_HAS_FLOAT16
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MATH_ERFF16_H
diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index 766feb0606c64..90067b7278c0c 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -138,6 +138,7 @@ add_math_entrypoint_object(dsubf128)
 
 add_math_entrypoint_object(erf)
 add_math_entrypoint_object(erff)
+add_math_entrypoint_object(erff16)
 
 add_math_entrypoint_object(exp)
 add_math_entrypoint_object(expf)
diff --git a/libc/src/math/erff16.h b/libc/src/math/erff16.h
new file mode 100644
index 0000000000000..922fe09df7911
--- /dev/null
+++ b/libc/src/math/erff16.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for erff16 ------------------------*- 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_ERFF16_H
+#define LLVM_LIBC_SRC_MATH_ERFF16_H
+
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+float16 erff16(float16 x);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_MATH_ERFF16_H
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index b9de548c8bedb..33b6b7e1197b8 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -1285,6 +1285,17 @@ add_entrypoint_object(
     libc.src.errno.errno
 )
 
+add_entrypoint_object(
+  erff16
+  SRCS
+    erff16.cpp
+  HDRS
+    ../erff16.h
+  DEPENDS
+    libc.src.__support.math.erff16
+    libc.src.errno.errno
+)
+
 add_entrypoint_object(
   exp
   SRCS
diff --git a/libc/src/math/generic/erff16.cpp b/libc/src/math/generic/erff16.cpp
new file mode 100644
index 0000000000000..d7e4f207a818e
--- /dev/null
+++ b/libc/src/math/generic/erff16.cpp
@@ -0,0 +1,16 @@
+//===-- Half-precision erf(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/erff16.h"
+#include "src/__support/math/erff16.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(float16, erff16, (float16 x)) { return math::erff16(x); }
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/shared/CMakeLists.txt b/libc/test/shared/CMakeLists.txt
index c90e5687d8c33..98417a99f240e 100644
--- a/libc/test/shared/CMakeLists.txt
+++ b/libc/test/shared/CMakeLists.txt
@@ -73,6 +73,7 @@ add_fp_unittest(
     libc.src.__support.math.exp10m1f
     libc.src.__support.math.exp10m1f16
     libc.src.__support.math.erff
+    libc.src.__support.math.erff16
     libc.src.__support.math.exp
     libc.src.__support.math.exp2
     libc.src.__support.math.exp2f
diff --git a/libc/test/shared/shared_math_test.cpp b/libc/test/shared/shared_math_test.cpp
index 17045ce5edfdb..9f7b121b323b3 100644
--- a/libc/test/shared/shared_math_test.cpp
+++ b/libc/test/shared/shared_math_test.cpp
@@ -29,6 +29,7 @@ TEST(LlvmLibcSharedMathTest, AllFloat16) {
   EXPECT_FP_EQ(0x1p+0f16, LIBC_NAMESPACE::shared::cosf16(0.0f16));
   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::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 90afe842e9dec..c2f9906be1358 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -2662,6 +2662,18 @@ add_fp_unittest(
     libc.src.__support.FPUtil.fp_bits
 )
 
+add_fp_unittest(
+  erff16_test
+  NEED_MPFR
+  SUITE
+    libc-math-unittests
+  SRCS
+    erff16_test.cpp
+  DEPENDS
+    libc.src.math.erff16
+    libc.src.__support.FPUtil.fp_bits
+)
+
 add_fp_unittest(
   pow_test
   NEED_MPFR
diff --git a/libc/test/src/math/erff16_test.cpp b/libc/test/src/math/erff16_test.cpp
new file mode 100644
index 0000000000000..522cc85ab15d6
--- /dev/null
+++ b/libc/test/src/math/erff16_test.cpp
@@ -0,0 +1,43 @@
+//===-- Exhaustive test for erff16 ----------------------------------------===//
+//
+// 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/erff16.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+
+using LlvmLibcErff16Test = 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(LlvmLibcErff16Test, PositiveRange) {
+  for (uint16_t v = POS_START; v <= POS_STOP; ++v) {
+    float16 x = FPBits(v).get_val();
+    EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Erf, x,
+                                   LIBC_NAMESPACE::erff16(x), 0.5);
+  }
+}
+
+TEST_F(LlvmLibcErff16Test, NegativeRange) {
+  for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) {
+    float16 x = FPBits(v).get_val();
+    EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Erf, x,
+                                   LIBC_NAMESPACE::erff16(x), 0.5);
+  }
+}
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index 185f646e3aa9c..b8724903d8776 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -5107,6 +5107,16 @@ add_fp_unittest(
     libc.src.__support.FPUtil.fp_bits
 )
 
+add_fp_unittest(
+  erff16_test
+  SUITE
+    libc-math-smoke-tests
+  SRCS
+    erff16_test.cpp
+  DEPENDS
+    libc.src.math.erff16
+)
+
 add_fp_unittest(
   pow_test
   SUITE
diff --git a/libc/test/src/math/smoke/erff16_test.cpp b/libc/test/src/math/smoke/erff16_test.cpp
new file mode 100644
index 0000000000000..71a6814fb5761
--- /dev/null
+++ b/libc/test/src/math/smoke/erff16_test.cpp
@@ -0,0 +1,51 @@
+//===-- Unittests for erff16 ----------------------------------------------===//
+//
+// 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/erff16.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcErffTest = LIBC_NAMESPACE::testing::FPTest<float16>;
+
+TEST_F(LlvmLibcErffTest, SpecialNumbers) {
+  EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::erff16(sNaN), FE_INVALID);
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::erff16(aNaN));
+  EXPECT_FP_EQ_ALL_ROUNDING(1.0f16, LIBC_NAMESPACE::erff16(inf));
+  EXPECT_FP_EQ_ALL_ROUNDING(-1.0f16, LIBC_NAMESPACE::erff16(neg_inf));
+  EXPECT_FP_EQ_ALL_ROUNDING(zero, LIBC_NAMESPACE::erff16(zero));
+  EXPECT_FP_EQ_ALL_ROUNDING(neg_zero, LIBC_NAMESPACE::erff16(neg_zero));
+}
+
+#ifdef LIBC_TEST_FTZ_DAZ
+
+using namespace LIBC_NAMESPACE::testing;
+
+TEST_F(LlvmLibcErffTest, FTZMode) {
+  ModifyMXCSR mxcsr(FTZ);
+  EXPECT_FP_EQ(0x1p-24f16, LIBC_NAMESPACE::erff16(min_denormal));
+  EXPECT_FP_EQ(0x1.208p-14f16, LIBC_NAMESPACE::erff16(max_denormal));
+}
+
+TEST_F(LlvmLibcErffTest, DAZMode) {
+  ModifyMXCSR mxcsr(DAZ);
+  EXPECT_FP_EQ(0x1p-24f16, LIBC_NAMESPACE::erff16(min_denormal));
+  EXPECT_FP_EQ(0x1.208p-14f16, LIBC_NAMESPACE::erff16(max_denormal));
+}
+
+TEST_F(LlvmLibcErffTest, FTZDAZMode) {
+  ModifyMXCSR mxcsr(FTZ | DAZ);
+
+  EXPECT_FP_EQ(0x1p-24f16, LIBC_NAMESPACE::erff16(min_denormal));
+  EXPECT_FP_EQ(0x1.208p-14f16, LIBC_NAMESPACE::erff16(max_denormal));
+}
+
+#endif
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index 9a0a70b13cae9..f26593d49e08f 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -3589,6 +3589,19 @@ libc_support_library(
         ":__support_fputil_polyeval",
         ":__support_macros_config",
         ":__support_macros_optimization",
+        ":__support_math_common_constants",
+    ],
+)
+
+libc_support_library(
+    name = "__support_math_erff16",
+    hdrs = ["src/__support/math/erff16.h"],
+    deps = [
+        ":__support_fputil_fenv_impl",
+        ":__support_fputil_fp_bits",
+        ":__support_fputil_multiply_add",
+        ":__support_macros_optimization",
+        ":__support_math_common_constants",
     ],
 )
 
@@ -6397,6 +6410,13 @@ libc_math_function(
     additional_deps = [":__support_math_erff"],
 )
 
+libc_math_function(
+    name = "erff16",
+    additional_deps = [
+        ":__support_math_erff16",
+    ],
+)
+
 libc_math_function(
     name = "exp",
     additional_deps = [

>From be63a63ad33cadc4a6703180fdb4dddf4cf6dcf2 Mon Sep 17 00:00:00 2001
From: Ezzeldin Ibrahim <ezzibrahimx at gmail.com>
Date: Sun, 15 Feb 2026 08:05:48 +0200
Subject: [PATCH 2/6] 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 d5a8026169a33..28608e8c327d1 100644
--- a/libc/config/windows/entrypoints.txt
+++ b/libc/config/windows/entrypoints.txt
@@ -166,7 +166,6 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.dfmal
     libc.src.math.dsubl
     libc.src.math.erff
-    libc.src.math.erff16
     libc.src.math.exp
     libc.src.math.expf
     libc.src.math.exp10

>From ad47fd6871dfb493665c6fe27d5f65bd6e3e919e Mon Sep 17 00:00:00 2001
From: Ezzeldin Ibrahim <ezzibrahimx at gmail.com>
Date: Tue, 17 Feb 2026 18:16:31 +0200
Subject: [PATCH 3/6] 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 38a6aae44f55f..d0e39c636f578 100644
--- a/libc/config/gpu/amdgpu/entrypoints.txt
+++ b/libc/config/gpu/amdgpu/entrypoints.txt
@@ -315,7 +315,6 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.dmull
     libc.src.math.dsqrtl
     libc.src.math.erff
-    libc.src.math.erff16
     libc.src.math.exp
     libc.src.math.exp10
     libc.src.math.exp10f
@@ -530,6 +529,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
     libc.src.math.cosf16
     libc.src.math.coshf16
     libc.src.math.cospif16
+    libc.src.math.erff16
     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 1ee0f01a4e3b4..34eaf7286d267 100644
--- a/libc/config/gpu/nvptx/entrypoints.txt
+++ b/libc/config/gpu/nvptx/entrypoints.txt
@@ -315,7 +315,6 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.dmull
     libc.src.math.dsqrtl
     libc.src.math.erff
-    libc.src.math.erff16
     libc.src.math.exp
     libc.src.math.exp10
     libc.src.math.exp10f
@@ -532,6 +531,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
     libc.src.math.cosf16
     libc.src.math.coshf16
     libc.src.math.cospif16
+    libc.src.math.erff16
     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 7aa3c28901672..d5981ecaaa19d 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -463,7 +463,6 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.dsqrtl
     libc.src.math.dsubl
     libc.src.math.erff
-    libc.src.math.erff16
     libc.src.math.exp
     libc.src.math.exp10
     libc.src.math.exp10f
@@ -680,6 +679,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
     libc.src.math.ceilf16
     libc.src.math.copysignf16
     libc.src.math.cospif16
+    libc.src.math.erff16
     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 6284136c47396..92ffe7e8d0bd5 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -285,7 +285,6 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.dfmal
     libc.src.math.dsqrtl
     libc.src.math.erff
-    libc.src.math.erff16
     libc.src.math.exp
     libc.src.math.exp10
     libc.src.math.exp10f
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index dff2d3ea25ec4..2590585de74b2 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -467,7 +467,6 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.dsqrtl
     libc.src.math.dsubl
     libc.src.math.erff
-    libc.src.math.erff16
     libc.src.math.exp
     libc.src.math.exp10
     libc.src.math.exp10f
@@ -694,6 +693,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
     libc.src.math.cosf16
     libc.src.math.coshf16
     libc.src.math.cospif16
+    libc.src.math.erff16
     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 51ba34d9034ad..f7dd1b7550209 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -512,7 +512,6 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.ddivl
     libc.src.math.dsubl
     libc.src.math.erff
-    libc.src.math.erff16
     libc.src.math.exp
     libc.src.math.exp10
     libc.src.math.exp10f
@@ -743,6 +742,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
     libc.src.math.cosf16
     libc.src.math.coshf16
     libc.src.math.cospif16
+    libc.src.math.erff16
     libc.src.math.exp10f16
     libc.src.math.exp10m1f16
     libc.src.math.exp2f16

>From 6988cc0876257323017d797fd62b9b5cd44d958d Mon Sep 17 00:00:00 2001
From: Ezzeldin Ibrahim <ezzibrahimx at gmail.com>
Date: Tue, 17 Feb 2026 18:17:57 +0200
Subject: [PATCH 4/6] remove static

---
 libc/src/__support/math/erff.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libc/src/__support/math/erff.h b/libc/src/__support/math/erff.h
index 339bb8089543b..61c30483bd9e3 100644
--- a/libc/src/__support/math/erff.h
+++ b/libc/src/__support/math/erff.h
@@ -20,7 +20,7 @@ namespace LIBC_NAMESPACE_DECL {
 
 namespace math {
 
-LIBC_INLINE static constexpr float erff(float x) {
+LIBC_INLINE constexpr float erff(float x) {
   using namespace common_constants_internal;
 
   using FPBits = typename fputil::FPBits<float>;

>From f04694f4224f29a65a619eb3929533d5d6587409 Mon Sep 17 00:00:00 2001
From: Anonmiraj <ezzibrahimx at gmail.com>
Date: Fri, 27 Feb 2026 21:11:45 +0200
Subject: [PATCH 5/6] fix nits

---
 libc/include/math.yaml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libc/include/math.yaml b/libc/include/math.yaml
index 8c333a2064379..143f85f6196f3 100644
--- a/libc/include/math.yaml
+++ b/libc/include/math.yaml
@@ -402,6 +402,7 @@ functions:
     return_type: _Float16
     arguments:
       - type: _Float16
+    guard: LIBC_TYPES_HAS_FLOAT16
   - name: exp
     standards:
       - stdc

>From 8e2b3a6698f9cfa3646fca095c1392b57cae704a Mon Sep 17 00:00:00 2001
From: Anonmiraj <ezzibrahimx at gmail.com>
Date: Thu, 12 Mar 2026 04:07:41 +0200
Subject: [PATCH 6/6] remove double usage in erff16

---
 libc/src/__support/math/CMakeLists.txt     |   1 -
 libc/src/__support/math/common_constants.h | 104 ---------------
 libc/src/__support/math/erff.h             | 105 ++++++++++++++-
 libc/src/__support/math/erff16.h           | 144 +++++++++++++++++----
 4 files changed, 221 insertions(+), 133 deletions(-)

diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt
index a7527a6d4cb06..6c50f23f1dadc 100644
--- a/libc/src/__support/math/CMakeLists.txt
+++ b/libc/src/__support/math/CMakeLists.txt
@@ -864,7 +864,6 @@ add_header_library(
   HDRS
     erff.h
   DEPENDS
-    .common_constants
     libc.src.__support.FPUtil.fp_bits
     libc.src.__support.FPUtil.except_value_utils
     libc.src.__support.FPUtil.multiply_add
diff --git a/libc/src/__support/math/common_constants.h b/libc/src/__support/math/common_constants.h
index ad1be400fcb86..4548cd506bb99 100644
--- a/libc/src/__support/math/common_constants.h
+++ b/libc/src/__support/math/common_constants.h
@@ -574,110 +574,6 @@ LIBC_INLINE_VAR constexpr double EXP_M2[128] = {
     0x1.568bb722dd593p1, 0x1.593b7d72305bbp1,
 };
 
-// Polynomials approximating erf(x)/x on ( k/8, (k + 1)/8 ) generated by
-// Sollya with: > P = fpminimax(erf(x)/x, [|0, 2, 4, 6, 8, 10, 12, 14|],
-// [|D...|],
-//                 [k/8, (k + 1)/8]);
-// for k = 0..31.
-constexpr double ERFF_COEFFS[32][8] = {
-    {0x1.20dd750429b6dp0, -0x1.812746b037753p-2, 0x1.ce2f219e8596ap-4,
-     -0x1.b82cdacb78fdap-6, 0x1.56479297dfda5p-8, -0x1.8b3ac5455ef02p-11,
-     -0x1.126fcac367e3bp-8, 0x1.2d0bdb3ba4984p-4},
-    {0x1.20dd750429b6dp0, -0x1.812746b0379a8p-2, 0x1.ce2f21a03cf2ap-4,
-     -0x1.b82ce30de083ep-6, 0x1.565bcad3eb60fp-8, -0x1.c02c66f659256p-11,
-     0x1.f92f673385229p-14, -0x1.def402648ae9p-17},
-    {0x1.20dd750429b34p0, -0x1.812746b032dcep-2, 0x1.ce2f219d84aaep-4,
-     -0x1.b82ce22dcf139p-6, 0x1.565b9efcd4af1p-8, -0x1.c021f1af414bcp-11,
-     0x1.f7c6d177eff82p-14, -0x1.c9e4410dcf865p-17},
-    {0x1.20dd750426eabp0, -0x1.812746ae592c7p-2, 0x1.ce2f211525f14p-4,
-     -0x1.b82ccc125e63fp-6, 0x1.56596f261cfd3p-8, -0x1.bfde1ff8eeecfp-11,
-     0x1.f31a9d15dc5d8p-14, -0x1.a5a4362844b3cp-17},
-    {0x1.20dd75039c705p0, -0x1.812746777e74dp-2, 0x1.ce2f17af98a1bp-4,
-     -0x1.b82be4b817cbep-6, 0x1.564bec2e2962ep-8, -0x1.bee86f9da3558p-11,
-     0x1.e9443689dc0ccp-14, -0x1.79c0f230805d8p-17},
-    {0x1.20dd74f811211p0, -0x1.81274371a3e8fp-2, 0x1.ce2ec038262e5p-4,
-     -0x1.b8265b82c5e1fp-6, 0x1.5615a2e239267p-8, -0x1.bc63ae023dcebp-11,
-     0x1.d87c2102f7e06p-14, -0x1.49584bea41d62p-17},
-    {0x1.20dd746d063e3p0, -0x1.812729a8a950fp-2, 0x1.ce2cb0a2df232p-4,
-     -0x1.b80eca1f51278p-6, 0x1.5572e26c46815p-8, -0x1.b715e5638b65ep-11,
-     0x1.bfbb195484968p-14, -0x1.177a565c15c52p-17},
-    {0x1.20dd701b44486p0, -0x1.812691145f237p-2, 0x1.ce23a06b8cfd9p-4,
-     -0x1.b7c1dc7245288p-6, 0x1.53e92f7f397ddp-8, -0x1.ad97cc4acf0b2p-11,
-     0x1.9f028b2b09b71p-14, -0x1.cdc4da08da8c1p-18},
-    {0x1.20dd5715ac332p0, -0x1.8123e680bd0ebp-2, 0x1.ce0457aded691p-4,
-     -0x1.b6f52d52bed4p-6, 0x1.50c291b84414cp-8, -0x1.9ea246b1ad4a9p-11,
-     0x1.77654674e0cap-14, -0x1.737c11a1bcebbp-18},
-    {0x1.20dce6593e114p0, -0x1.811a59c02eadcp-2, 0x1.cdab53c7cd7d5p-4,
-     -0x1.b526d2e321eedp-6, 0x1.4b1d32cd8b994p-8, -0x1.8963143ec0a1ep-11,
-     0x1.4ad5700e4db91p-14, -0x1.231e100e43ef2p-18},
-    {0x1.20db48bfd5a62p0, -0x1.80fdd84f9e308p-2, 0x1.ccd340d462983p-4,
-     -0x1.b196a2928768p-6, 0x1.4210c2c13a0f7p-8, -0x1.6dbdfb4ff71aep-11,
-     0x1.1bca2d17fbd71p-14, -0x1.bca36f90c7cf5p-19},
-    {0x1.20d64b2f8f508p0, -0x1.80b4d4f19fa8bp-2, 0x1.cb088197262e3p-4,
-     -0x1.ab51fd02e5b99p-6, 0x1.34e1e5e81a632p-8, -0x1.4c66377b502cep-11,
-     0x1.d9ad25066213cp-15, -0x1.4b0df7dd0cfa1p-19},
-    {0x1.20c8fc1243576p0, -0x1.8010cb2009e27p-2, 0x1.c7a47e9299315p-4,
-     -0x1.a155be5683654p-6, 0x1.233502694997bp-8, -0x1.26c94b7d813p-11,
-     0x1.8094f1de25fb9p-15, -0x1.e0e3d776c6eefp-20},
-    {0x1.20a9bd1611bc1p0, -0x1.7ec7fbce83f9p-2, 0x1.c1d757d7317b7p-4,
-     -0x1.92c160cd589fp-6, 0x1.0d307269cc5c2p-8, -0x1.fda5b0d2d1879p-12,
-     0x1.2fdd7b3b14a7fp-15, -0x1.54eed4a26af5ap-20},
-    {0x1.20682834f943dp0, -0x1.7c73f747bf5a9p-2, 0x1.b8c2db4a9ffd1p-4,
-     -0x1.7f0e4ffe989ecp-6, 0x1.e7061eae4166ep-9, -0x1.ad36e873fff2dp-12,
-     0x1.d39222396128ep-16, -0x1.d83dacec5ea6bp-21},
-    {0x1.1feb8d12676d7p0, -0x1.7898347284afep-2, 0x1.aba3466b34451p-4,
-     -0x1.663adc573e2f9p-6, 0x1.ae99fb17c3e08p-9, -0x1.602f950ad5535p-12,
-     0x1.5e9717490609dp-16, -0x1.3fca107bbc8d5p-21},
-    {0x1.1f12fe3c536fap0, -0x1.72b1d1f22e6d3p-2, 0x1.99fc0eed4a896p-4,
-     -0x1.48db0a87bd8c6p-6, 0x1.73e368895aa61p-9, -0x1.19b35d5301fc8p-12,
-     0x1.007987e4bb033p-16, -0x1.a7edcd4c2dc7p-22},
-    {0x1.1db7b0df84d5dp0, -0x1.6a4e4a41cde02p-2, 0x1.83bbded16455dp-4,
-     -0x1.2809b3b36977ep-6, 0x1.39c08bab44679p-9, -0x1.b7b45a70ed119p-13,
-     0x1.6e99b36410e7bp-17, -0x1.13619bb7ebc0cp-22},
-    {0x1.1bb1c85c4a527p0, -0x1.5f23b99a249a3p-2, 0x1.694c91fa0d12cp-4,
-     -0x1.053e1ce11c72dp-6, 0x1.02bf72c50ea78p-9, -0x1.4f478fb56cb02p-13,
-     0x1.005f80ecbe213p-17, -0x1.5f2446bde7f5bp-23},
-    {0x1.18dec3bd51f9dp0, -0x1.5123f58346186p-2, 0x1.4b8a1ca536ab4p-4,
-     -0x1.c4243015cc723p-7, 0x1.a1a8a01d351efp-10, -0x1.f466b34f1d86bp-14,
-     0x1.5f835eea0bf6ap-18, -0x1.b83165b939234p-24},
-    {0x1.152804c3369f4p0, -0x1.4084cd4afd4bcp-2, 0x1.2ba2e836e47aap-4,
-     -0x1.800f2dfc6904bp-7, 0x1.4a6daf0669c59p-10, -0x1.6e326ab872317p-14,
-     0x1.d9761a6a755a5p-19, -0x1.0fca33f9dd4b5p-24},
-    {0x1.1087ad68356aap0, -0x1.2dbb044707459p-2, 0x1.0aea8ceaa0384p-4,
-     -0x1.40b516d52b3d2p-7, 0x1.00c9e05f01d22p-10, -0x1.076afb0dc0ff7p-14,
-     0x1.39fadec400657p-19, -0x1.4b5761352e7e3p-25},
-    {0x1.0b0a7a8ba4a22p0, -0x1.196990d22d4a1p-2, 0x1.d5551e6ac0c4dp-5,
-     -0x1.07cce1770bd1ap-7, 0x1.890347b8848bfp-11, -0x1.757ec96750b6ap-15,
-     0x1.9b258a1e06bcep-20, -0x1.8fc6d22da7572p-26},
-    {0x1.04ce2be70fb47p0, -0x1.0449e4b0b9cacp-2, 0x1.97f7424f4b0e7p-5,
-     -0x1.ac825439c42f4p-8, 0x1.28f5f65426dfbp-11, -0x1.05b699a90f90fp-15,
-     0x1.0a888eecf4593p-20, -0x1.deace2b32bb31p-27},
-    {0x1.fbf9fb0e11cc8p-1, -0x1.de2640856545ap-3, 0x1.5f5b1f47f851p-5,
-     -0x1.588bc71eb41b9p-8, 0x1.bc6a0a772f56dp-12, -0x1.6b9fad1f1657ap-16,
-     0x1.573204ba66504p-21, -0x1.1d38065c94e44p-27},
-    {0x1.ed8f18c99e031p-1, -0x1.b4cb6acd903b4p-3, 0x1.2c7f3dddd6fc1p-5,
-     -0x1.13052067df4ep-8, 0x1.4a5027444082fp-12, -0x1.f672bab0e2554p-17,
-     0x1.b83c756348cc9p-22, -0x1.534f1a1079499p-28},
-    {0x1.debd33044166dp-1, -0x1.8d7cd9053f7d8p-3, 0x1.ff9957fb3d6e7p-6,
-     -0x1.b50be55de0f36p-9, 0x1.e92c8ec53a628p-13, -0x1.5a4b88d508007p-17,
-     0x1.1a27737559e26p-22, -0x1.942ae62cb2c14p-29},
-    {0x1.cfdbf0386f3bdp-1, -0x1.68e33d93b0dc4p-3, 0x1.b2683d58f53dep-6,
-     -0x1.5a9174e70d26fp-9, 0x1.69ddd326d49cdp-13, -0x1.dd8f397a8219cp-18,
-     0x1.6a755016ad4ddp-23, -0x1.e366e0139187dp-30},
-    {0x1.c132adb8d7464p-1, -0x1.475a899f61b46p-3, 0x1.70a431397a77cp-6,
-     -0x1.12e3d35beeee2p-9, 0x1.0c16b05738333p-13, -0x1.4a47f873e144ep-18,
-     0x1.d3d494c698c02p-24, -0x1.2302c59547fe5p-30},
-    {0x1.b2f5fd05555e7p-1, -0x1.28feefbe03ec7p-3, 0x1.3923acbb3a676p-6,
-     -0x1.b4ff793cd6358p-10, 0x1.8ea0eb8c913bcp-14, -0x1.cb31ec2baceb1p-19,
-     0x1.30011e7e80c04p-24, -0x1.617710635cb1dp-31},
-    {0x1.a54853cd9593ep-1, -0x1.0dbdbaea4dc8ep-3, 0x1.0a93e2c20a0fdp-6,
-     -0x1.5c969ff401ea8p-10, 0x1.29e0cc64fe627p-14, -0x1.4160d8e9d3c2ap-19,
-     0x1.8e7b67594624ap-25, -0x1.b1cf2c975b09bp-32},
-    {0x1.983ceece09ff8p-1, -0x1.eacc78f7a2dp-4, 0x1.c74418410655fp-7,
-     -0x1.1756a050e441ep-10, 0x1.bff3650f7f548p-15, -0x1.c56c0217d3adap-20,
-     0x1.07b4918d0b489p-25, -0x1.0d4be8c1c50f8p-32},
-};
-
 } // namespace common_constants_internal
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/__support/math/erff.h b/libc/src/__support/math/erff.h
index 61c30483bd9e3..cf347ea260864 100644
--- a/libc/src/__support/math/erff.h
+++ b/libc/src/__support/math/erff.h
@@ -9,7 +9,6 @@
 #ifndef LLVM_LIBC_SRC___SUPPORT_MATH_ERFF_H
 #define LLVM_LIBC_SRC___SUPPORT_MATH_ERFF_H
 
-#include "common_constants.h" // ERFF_COEFFS
 #include "src/__support/FPUtil/FPBits.h"
 #include "src/__support/FPUtil/except_value_utils.h"
 #include "src/__support/FPUtil/multiply_add.h"
@@ -21,8 +20,110 @@ namespace LIBC_NAMESPACE_DECL {
 namespace math {
 
 LIBC_INLINE constexpr float erff(float x) {
-  using namespace common_constants_internal;
 
+  // Polynomials approximating erf(x)/x on ( k/8, (k + 1)/8 ) generated by
+  // Sollya with: > P = fpminimax(erf(x)/x, [|0, 2, 4, 6, 8, 10, 12, 14|],
+  // [|D...|],
+  //                 [k/8, (k + 1)/8]);
+  // for k = 0..31.
+  constexpr double ERFF_COEFFS[32][8] = {
+      {0x1.20dd750429b6dp0, -0x1.812746b037753p-2, 0x1.ce2f219e8596ap-4,
+       -0x1.b82cdacb78fdap-6, 0x1.56479297dfda5p-8, -0x1.8b3ac5455ef02p-11,
+       -0x1.126fcac367e3bp-8, 0x1.2d0bdb3ba4984p-4},
+      {0x1.20dd750429b6dp0, -0x1.812746b0379a8p-2, 0x1.ce2f21a03cf2ap-4,
+       -0x1.b82ce30de083ep-6, 0x1.565bcad3eb60fp-8, -0x1.c02c66f659256p-11,
+       0x1.f92f673385229p-14, -0x1.def402648ae9p-17},
+      {0x1.20dd750429b34p0, -0x1.812746b032dcep-2, 0x1.ce2f219d84aaep-4,
+       -0x1.b82ce22dcf139p-6, 0x1.565b9efcd4af1p-8, -0x1.c021f1af414bcp-11,
+       0x1.f7c6d177eff82p-14, -0x1.c9e4410dcf865p-17},
+      {0x1.20dd750426eabp0, -0x1.812746ae592c7p-2, 0x1.ce2f211525f14p-4,
+       -0x1.b82ccc125e63fp-6, 0x1.56596f261cfd3p-8, -0x1.bfde1ff8eeecfp-11,
+       0x1.f31a9d15dc5d8p-14, -0x1.a5a4362844b3cp-17},
+      {0x1.20dd75039c705p0, -0x1.812746777e74dp-2, 0x1.ce2f17af98a1bp-4,
+       -0x1.b82be4b817cbep-6, 0x1.564bec2e2962ep-8, -0x1.bee86f9da3558p-11,
+       0x1.e9443689dc0ccp-14, -0x1.79c0f230805d8p-17},
+      {0x1.20dd74f811211p0, -0x1.81274371a3e8fp-2, 0x1.ce2ec038262e5p-4,
+       -0x1.b8265b82c5e1fp-6, 0x1.5615a2e239267p-8, -0x1.bc63ae023dcebp-11,
+       0x1.d87c2102f7e06p-14, -0x1.49584bea41d62p-17},
+      {0x1.20dd746d063e3p0, -0x1.812729a8a950fp-2, 0x1.ce2cb0a2df232p-4,
+       -0x1.b80eca1f51278p-6, 0x1.5572e26c46815p-8, -0x1.b715e5638b65ep-11,
+       0x1.bfbb195484968p-14, -0x1.177a565c15c52p-17},
+      {0x1.20dd701b44486p0, -0x1.812691145f237p-2, 0x1.ce23a06b8cfd9p-4,
+       -0x1.b7c1dc7245288p-6, 0x1.53e92f7f397ddp-8, -0x1.ad97cc4acf0b2p-11,
+       0x1.9f028b2b09b71p-14, -0x1.cdc4da08da8c1p-18},
+      {0x1.20dd5715ac332p0, -0x1.8123e680bd0ebp-2, 0x1.ce0457aded691p-4,
+       -0x1.b6f52d52bed4p-6, 0x1.50c291b84414cp-8, -0x1.9ea246b1ad4a9p-11,
+       0x1.77654674e0cap-14, -0x1.737c11a1bcebbp-18},
+      {0x1.20dce6593e114p0, -0x1.811a59c02eadcp-2, 0x1.cdab53c7cd7d5p-4,
+       -0x1.b526d2e321eedp-6, 0x1.4b1d32cd8b994p-8, -0x1.8963143ec0a1ep-11,
+       0x1.4ad5700e4db91p-14, -0x1.231e100e43ef2p-18},
+      {0x1.20db48bfd5a62p0, -0x1.80fdd84f9e308p-2, 0x1.ccd340d462983p-4,
+       -0x1.b196a2928768p-6, 0x1.4210c2c13a0f7p-8, -0x1.6dbdfb4ff71aep-11,
+       0x1.1bca2d17fbd71p-14, -0x1.bca36f90c7cf5p-19},
+      {0x1.20d64b2f8f508p0, -0x1.80b4d4f19fa8bp-2, 0x1.cb088197262e3p-4,
+       -0x1.ab51fd02e5b99p-6, 0x1.34e1e5e81a632p-8, -0x1.4c66377b502cep-11,
+       0x1.d9ad25066213cp-15, -0x1.4b0df7dd0cfa1p-19},
+      {0x1.20c8fc1243576p0, -0x1.8010cb2009e27p-2, 0x1.c7a47e9299315p-4,
+       -0x1.a155be5683654p-6, 0x1.233502694997bp-8, -0x1.26c94b7d813p-11,
+       0x1.8094f1de25fb9p-15, -0x1.e0e3d776c6eefp-20},
+      {0x1.20a9bd1611bc1p0, -0x1.7ec7fbce83f9p-2, 0x1.c1d757d7317b7p-4,
+       -0x1.92c160cd589fp-6, 0x1.0d307269cc5c2p-8, -0x1.fda5b0d2d1879p-12,
+       0x1.2fdd7b3b14a7fp-15, -0x1.54eed4a26af5ap-20},
+      {0x1.20682834f943dp0, -0x1.7c73f747bf5a9p-2, 0x1.b8c2db4a9ffd1p-4,
+       -0x1.7f0e4ffe989ecp-6, 0x1.e7061eae4166ep-9, -0x1.ad36e873fff2dp-12,
+       0x1.d39222396128ep-16, -0x1.d83dacec5ea6bp-21},
+      {0x1.1feb8d12676d7p0, -0x1.7898347284afep-2, 0x1.aba3466b34451p-4,
+       -0x1.663adc573e2f9p-6, 0x1.ae99fb17c3e08p-9, -0x1.602f950ad5535p-12,
+       0x1.5e9717490609dp-16, -0x1.3fca107bbc8d5p-21},
+      {0x1.1f12fe3c536fap0, -0x1.72b1d1f22e6d3p-2, 0x1.99fc0eed4a896p-4,
+       -0x1.48db0a87bd8c6p-6, 0x1.73e368895aa61p-9, -0x1.19b35d5301fc8p-12,
+       0x1.007987e4bb033p-16, -0x1.a7edcd4c2dc7p-22},
+      {0x1.1db7b0df84d5dp0, -0x1.6a4e4a41cde02p-2, 0x1.83bbded16455dp-4,
+       -0x1.2809b3b36977ep-6, 0x1.39c08bab44679p-9, -0x1.b7b45a70ed119p-13,
+       0x1.6e99b36410e7bp-17, -0x1.13619bb7ebc0cp-22},
+      {0x1.1bb1c85c4a527p0, -0x1.5f23b99a249a3p-2, 0x1.694c91fa0d12cp-4,
+       -0x1.053e1ce11c72dp-6, 0x1.02bf72c50ea78p-9, -0x1.4f478fb56cb02p-13,
+       0x1.005f80ecbe213p-17, -0x1.5f2446bde7f5bp-23},
+      {0x1.18dec3bd51f9dp0, -0x1.5123f58346186p-2, 0x1.4b8a1ca536ab4p-4,
+       -0x1.c4243015cc723p-7, 0x1.a1a8a01d351efp-10, -0x1.f466b34f1d86bp-14,
+       0x1.5f835eea0bf6ap-18, -0x1.b83165b939234p-24},
+      {0x1.152804c3369f4p0, -0x1.4084cd4afd4bcp-2, 0x1.2ba2e836e47aap-4,
+       -0x1.800f2dfc6904bp-7, 0x1.4a6daf0669c59p-10, -0x1.6e326ab872317p-14,
+       0x1.d9761a6a755a5p-19, -0x1.0fca33f9dd4b5p-24},
+      {0x1.1087ad68356aap0, -0x1.2dbb044707459p-2, 0x1.0aea8ceaa0384p-4,
+       -0x1.40b516d52b3d2p-7, 0x1.00c9e05f01d22p-10, -0x1.076afb0dc0ff7p-14,
+       0x1.39fadec400657p-19, -0x1.4b5761352e7e3p-25},
+      {0x1.0b0a7a8ba4a22p0, -0x1.196990d22d4a1p-2, 0x1.d5551e6ac0c4dp-5,
+       -0x1.07cce1770bd1ap-7, 0x1.890347b8848bfp-11, -0x1.757ec96750b6ap-15,
+       0x1.9b258a1e06bcep-20, -0x1.8fc6d22da7572p-26},
+      {0x1.04ce2be70fb47p0, -0x1.0449e4b0b9cacp-2, 0x1.97f7424f4b0e7p-5,
+       -0x1.ac825439c42f4p-8, 0x1.28f5f65426dfbp-11, -0x1.05b699a90f90fp-15,
+       0x1.0a888eecf4593p-20, -0x1.deace2b32bb31p-27},
+      {0x1.fbf9fb0e11cc8p-1, -0x1.de2640856545ap-3, 0x1.5f5b1f47f851p-5,
+       -0x1.588bc71eb41b9p-8, 0x1.bc6a0a772f56dp-12, -0x1.6b9fad1f1657ap-16,
+       0x1.573204ba66504p-21, -0x1.1d38065c94e44p-27},
+      {0x1.ed8f18c99e031p-1, -0x1.b4cb6acd903b4p-3, 0x1.2c7f3dddd6fc1p-5,
+       -0x1.13052067df4ep-8, 0x1.4a5027444082fp-12, -0x1.f672bab0e2554p-17,
+       0x1.b83c756348cc9p-22, -0x1.534f1a1079499p-28},
+      {0x1.debd33044166dp-1, -0x1.8d7cd9053f7d8p-3, 0x1.ff9957fb3d6e7p-6,
+       -0x1.b50be55de0f36p-9, 0x1.e92c8ec53a628p-13, -0x1.5a4b88d508007p-17,
+       0x1.1a27737559e26p-22, -0x1.942ae62cb2c14p-29},
+      {0x1.cfdbf0386f3bdp-1, -0x1.68e33d93b0dc4p-3, 0x1.b2683d58f53dep-6,
+       -0x1.5a9174e70d26fp-9, 0x1.69ddd326d49cdp-13, -0x1.dd8f397a8219cp-18,
+       0x1.6a755016ad4ddp-23, -0x1.e366e0139187dp-30},
+      {0x1.c132adb8d7464p-1, -0x1.475a899f61b46p-3, 0x1.70a431397a77cp-6,
+       -0x1.12e3d35beeee2p-9, 0x1.0c16b05738333p-13, -0x1.4a47f873e144ep-18,
+       0x1.d3d494c698c02p-24, -0x1.2302c59547fe5p-30},
+      {0x1.b2f5fd05555e7p-1, -0x1.28feefbe03ec7p-3, 0x1.3923acbb3a676p-6,
+       -0x1.b4ff793cd6358p-10, 0x1.8ea0eb8c913bcp-14, -0x1.cb31ec2baceb1p-19,
+       0x1.30011e7e80c04p-24, -0x1.617710635cb1dp-31},
+      {0x1.a54853cd9593ep-1, -0x1.0dbdbaea4dc8ep-3, 0x1.0a93e2c20a0fdp-6,
+       -0x1.5c969ff401ea8p-10, 0x1.29e0cc64fe627p-14, -0x1.4160d8e9d3c2ap-19,
+       0x1.8e7b67594624ap-25, -0x1.b1cf2c975b09bp-32},
+      {0x1.983ceece09ff8p-1, -0x1.eacc78f7a2dp-4, 0x1.c74418410655fp-7,
+       -0x1.1756a050e441ep-10, 0x1.bff3650f7f548p-15, -0x1.c56c0217d3adap-20,
+       0x1.07b4918d0b489p-25, -0x1.0d4be8c1c50f8p-32},
+  };
   using FPBits = typename fputil::FPBits<float>;
   FPBits xbits(x);
 
diff --git a/libc/src/__support/math/erff16.h b/libc/src/__support/math/erff16.h
index fa0cd97335cdf..99ca357b1d5d7 100644
--- a/libc/src/__support/math/erff16.h
+++ b/libc/src/__support/math/erff16.h
@@ -13,9 +13,9 @@
 
 #ifdef LIBC_TYPES_HAS_FLOAT16
 
-#include "common_constants.h" // ERFF_COEFFS
 #include "src/__support/FPUtil/FEnvImpl.h"
 #include "src/__support/FPUtil/FPBits.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
@@ -25,11 +25,103 @@ namespace LIBC_NAMESPACE_DECL {
 namespace math {
 
 LIBC_INLINE float16 erff16(float16 x) {
-  using namespace common_constants_internal;
 
+  // Polynomials approximating erf(x)/x on (k/8, (k+1)/8) for k = 0..31
+  // Generated by truncating ERFF_COEFFS (double) to single precision
+  // P(x^2) where P has coefficients c0, c1, ..., c7
+  // erf(x) ~ x * (c0 + c1*x^2 + c2*x^4 + ... + c7*x^14)
+  constexpr float ERFF16_COEFFS[32][8] = {
+      {0x1.20dd76p+0f, -0x1.812746p-2f, 0x1.ce2f22p-4f, -0x1.b82cdap-6f,
+       0x1.564792p-8f, -0x1.8b3ac6p-11f, -0x1.126fcap-8f, 0x1.2d0bdcp-4f},
+      {0x1.20dd76p+0f, -0x1.812746p-2f, 0x1.ce2f22p-4f, -0x1.b82ce4p-6f,
+       0x1.565bcap-8f, -0x1.c02c66p-11f, 0x1.f92f68p-14f, -0x1.def402p-17f},
+      {0x1.20dd76p+0f, -0x1.812746p-2f, 0x1.ce2f22p-4f, -0x1.b82ce2p-6f,
+       0x1.565b9ep-8f, -0x1.c021f2p-11f, 0x1.f7c6d2p-14f, -0x1.c9e442p-17f},
+      {0x1.20dd76p+0f, -0x1.812746p-2f, 0x1.ce2f22p-4f, -0x1.b82cccp-6f,
+       0x1.56597p-8f, -0x1.bfde2p-11f, 0x1.f31a9ep-14f, -0x1.a5a436p-17f},
+      {0x1.20dd76p+0f, -0x1.812746p-2f, 0x1.ce2f18p-4f, -0x1.b82be4p-6f,
+       0x1.564becp-8f, -0x1.bee87p-11f, 0x1.e94436p-14f, -0x1.79c0f2p-17f},
+      {0x1.20dd74p+0f, -0x1.812744p-2f, 0x1.ce2ecp-4f, -0x1.b8265cp-6f,
+       0x1.5615a2p-8f, -0x1.bc63aep-11f, 0x1.d87c22p-14f, -0x1.49584cp-17f},
+      {0x1.20dd74p+0f, -0x1.81272ap-2f, 0x1.ce2cbp-4f, -0x1.b80ecap-6f,
+       0x1.5572e2p-8f, -0x1.b715e6p-11f, 0x1.bfbb1ap-14f, -0x1.177a56p-17f},
+      {0x1.20dd7p+0f, -0x1.812692p-2f, 0x1.ce23ap-4f, -0x1.b7c1dcp-6f,
+       0x1.53e93p-8f, -0x1.ad97ccp-11f, 0x1.9f028cp-14f, -0x1.cdc4dap-18f},
+      {0x1.20dd58p+0f, -0x1.8123e6p-2f, 0x1.ce0458p-4f, -0x1.b6f52ep-6f,
+       0x1.50c292p-8f, -0x1.9ea246p-11f, 0x1.776546p-14f, -0x1.737c12p-18f},
+      {0x1.20dce6p+0f, -0x1.811a5ap-2f, 0x1.cdab54p-4f, -0x1.b526d2p-6f,
+       0x1.4b1d32p-8f, -0x1.896314p-11f, 0x1.4ad57p-14f, -0x1.231e1p-18f},
+      {0x1.20db48p+0f, -0x1.80fdd8p-2f, 0x1.ccd34p-4f, -0x1.b196a2p-6f,
+       0x1.4210c2p-8f, -0x1.6dbdfcp-11f, 0x1.1bca2ep-14f, -0x1.bca37p-19f},
+      {0x1.20d64cp+0f, -0x1.80b4d4p-2f, 0x1.cb0882p-4f, -0x1.ab51fep-6f,
+       0x1.34e1e6p-8f, -0x1.4c6638p-11f, 0x1.d9ad26p-15f, -0x1.4b0df8p-19f},
+      {0x1.20c8fcp+0f, -0x1.8010ccp-2f, 0x1.c7a47ep-4f, -0x1.a155bep-6f,
+       0x1.233502p-8f, -0x1.26c94cp-11f, 0x1.8094f2p-15f, -0x1.e0e3d8p-20f},
+      {0x1.20a9bep+0f, -0x1.7ec7fcp-2f, 0x1.c1d758p-4f, -0x1.92c16p-6f,
+       0x1.0d3072p-8f, -0x1.fda5bp-12f, 0x1.2fdd7cp-15f, -0x1.54eed4p-20f},
+      {0x1.206828p+0f, -0x1.7c73f8p-2f, 0x1.b8c2dcp-4f, -0x1.7f0e5p-6f,
+       0x1.e7061ep-9f, -0x1.ad36e8p-12f, 0x1.d39222p-16f, -0x1.d83dacp-21f},
+      {0x1.1feb8ep+0f, -0x1.789834p-2f, 0x1.aba346p-4f, -0x1.663adcp-6f,
+       0x1.ae99fcp-9f, -0x1.602f96p-12f, 0x1.5e9718p-16f, -0x1.3fca1p-21f},
+      {0x1.1f12fep+0f, -0x1.72b1d2p-2f, 0x1.99fc0ep-4f, -0x1.48db0ap-6f,
+       0x1.73e368p-9f, -0x1.19b35ep-12f, 0x1.007988p-16f, -0x1.a7edcep-22f},
+      {0x1.1db7bp+0f, -0x1.6a4e4ap-2f, 0x1.83bbdep-4f, -0x1.2809b4p-6f,
+       0x1.39c08cp-9f, -0x1.b7b45ap-13f, 0x1.6e99b4p-17f, -0x1.13619cp-22f},
+      {0x1.1bb1c8p+0f, -0x1.5f23bap-2f, 0x1.694c92p-4f, -0x1.053e1cp-6f,
+       0x1.02bf72p-9f, -0x1.4f479p-13f, 0x1.005f8p-17f, -0x1.5f2446p-23f},
+      {0x1.18dec4p+0f, -0x1.5123f6p-2f, 0x1.4b8a1cp-4f, -0x1.c4243p-7f,
+       0x1.a1a8ap-10f, -0x1.f466b4p-14f, 0x1.5f835ep-18f, -0x1.b83166p-24f},
+      {0x1.152804p+0f, -0x1.4084cep-2f, 0x1.2ba2e8p-4f, -0x1.800f2ep-7f,
+       0x1.4a6dbp-10f, -0x1.6e326ap-14f, 0x1.d9761ap-19f, -0x1.0fca34p-24f},
+      {0x1.1087aep+0f, -0x1.2dbb04p-2f, 0x1.0aea8cp-4f, -0x1.40b516p-7f,
+       0x1.00c9ep-10f, -0x1.076afcp-14f, 0x1.39fadep-19f, -0x1.4b5762p-25f},
+      {0x1.0b0a7ap+0f, -0x1.19699p-2f, 0x1.d5551ep-5f, -0x1.07cce2p-7f,
+       0x1.890348p-11f, -0x1.757ecap-15f, 0x1.9b258ap-20f, -0x1.8fc6d2p-26f},
+      {0x1.04ce2cp+0f, -0x1.0449e4p-2f, 0x1.97f742p-5f, -0x1.ac8254p-8f,
+       0x1.28f5f6p-11f, -0x1.05b69ap-15f, 0x1.0a888ep-20f, -0x1.deace2p-27f},
+      {0x1.fbf9fcp-1f, -0x1.de264p-3f, 0x1.5f5b2p-5f, -0x1.588bc8p-8f,
+       0x1.bc6a0ap-12f, -0x1.6b9faep-16f, 0x1.573204p-21f, -0x1.1d3806p-27f},
+      {0x1.ed8f18p-1f, -0x1.b4cb6ap-3f, 0x1.2c7f3ep-5f, -0x1.13052p-8f,
+       0x1.4a5028p-12f, -0x1.f672bap-17f, 0x1.b83c76p-22f, -0x1.534f1ap-28f},
+      {0x1.debd34p-1f, -0x1.8d7cdap-3f, 0x1.ff9958p-6f, -0x1.b50be6p-9f,
+       0x1.e92c8ep-13f, -0x1.5a4b88p-17f, 0x1.1a2774p-22f, -0x1.942ae6p-29f},
+      {0x1.cfdbfp-1f, -0x1.68e33ep-3f, 0x1.b2683ep-6f, -0x1.5a9174p-9f,
+       0x1.69ddd4p-13f, -0x1.dd8f3ap-18f, 0x1.6a755p-23f, -0x1.e366ep-30f},
+      {0x1.c132aep-1f, -0x1.475a8ap-3f, 0x1.70a432p-6f, -0x1.12e3d4p-9f,
+       0x1.0c16bp-13f, -0x1.4a47f8p-18f, 0x1.d3d494p-24f, -0x1.2302c6p-30f},
+      {0x1.b2f5fep-1f, -0x1.28fefp-3f, 0x1.3923acp-6f, -0x1.b4ff7ap-10f,
+       0x1.8ea0ecp-14f, -0x1.cb31ecp-19f, 0x1.30011ep-24f, -0x1.61771p-31f},
+      {0x1.a54854p-1f, -0x1.0dbdbap-3f, 0x1.0a93e2p-6f, -0x1.5c96ap-10f,
+       0x1.29e0ccp-14f, -0x1.4160d8p-19f, 0x1.8e7b68p-25f, -0x1.b1cf2cp-32f},
+      {0x1.983ceep-1f, -0x1.eacc78p-4f, 0x1.c74418p-7f, -0x1.1756ap-10f,
+       0x1.bff366p-15f, -0x1.c56c02p-20f, 0x1.07b492p-25f, -0x1.0d4be8p-32f},
+  };
+  static constexpr size_t N_ERFF16_EXCEPTS = 13;
+  static constexpr fputil::ExceptValues<float16, N_ERFF16_EXCEPTS>
+      ERFF16_EXCEPTS{{
+          {0x1612, 0x16D9, 1, 0, 0},
+          {0x165C, 0x172C, 1, 0, 1},
+          {0x16F0, 0x17D3, 1, 0, 1},
+          {0x3BF2, 0x3AB7, 1, 0, 1},
+          {0x42A9, 0x3BFF, 1, 0, 1},
+          {0x42BD, 0x3BFF, 1, 0, 1},
+          {0x42F0, 0x3BFF, 1, 0, 1},
+          {0x4344, 0x3BFF, 1, 0, 1},
+          {0x43D3, 0x3BFF, 1, 0, 1},
+          {0x43D4, 0x3BFF, 1, 0, 1},
+          {0x43D6, 0x3BFF, 1, 0, 1},
+          {0x43D8, 0x3BFF, 1, 0, 1},
+          {0x43F7, 0x3BFF, 1, 0, 1},
+      }};
   using FPBits = typename fputil::FPBits<float16>;
   FPBits xbits(x);
   uint16_t x_abs = xbits.abs().uintval();
+  bool is_neg = xbits.is_neg();
+
+  float xf = fputil::cast<float16>(x);
+  if (auto r = ERFF16_EXCEPTS.lookup_odd(x_abs, is_neg);
+      LIBC_UNLIKELY(r.has_value()))
+    return r.value();
 
   // |x| >= 4.0
   if (LIBC_UNLIKELY(x_abs >= 0x4400U)) {
@@ -43,42 +135,42 @@ LIBC_INLINE float16 erff16(float16 x) {
         return x;
       }
       // Inf -> returns 1.0 or -1.0
-      return xbits.is_neg() ? -1.0f16 : 1.0f16;
+      return is_neg ? -1.0f16 : 1.0f16;
     }
 
-    return static_cast<float16>(xbits.is_neg() ? -1.0 - x * 0x1.0p-50
-                                               : 1.0 - x * 0x1.0p-50);
+    return fputil::cast<float16>(is_neg ? -1.0 - xf * 0x1.0p-28f
+                                        : 1.0 - xf * 0x1.0p-28f);
   }
 
   // Polynomial approximation:
   //   erf(x) ~ x * (c0 + c1 * x^2 + c2 * x^4 + ... + c7 * x^14)
 
-  using FPBits32 = typename fputil::FPBits<float>;
-  float xf = x;
-  FPBits32 xbits32(xf);
-  uint32_t x_abs32 = xbits32.abs().uintval();
+  int idx = static_cast<int>(xbits.abs().get_val() * 8.0f16);
 
-  constexpr uint32_t EIGHT = 3 << FPBits32::FRACTION_LEN;
-  int idx = static_cast<int>(FPBits32(x_abs32 + EIGHT).get_val());
+  float xsq = xf * xf;
+  float x4 = xsq * xsq;
+  float c0 = fputil::multiply_add(xsq, (ERFF16_COEFFS[idx][1]),
+                                  (ERFF16_COEFFS[idx][0]));
+  float c1 = fputil::multiply_add(xsq, (ERFF16_COEFFS[idx][3]),
+                                  (ERFF16_COEFFS[idx][2]));
+  float c2 = fputil::multiply_add(xsq, (ERFF16_COEFFS[idx][5]),
+                                  (ERFF16_COEFFS[idx][4]));
+  float c3 = fputil::multiply_add(xsq, (ERFF16_COEFFS[idx][7]),
+                                  (ERFF16_COEFFS[idx][6]));
 
-  double xd = static_cast<double>(x);
-  double xsq = xd * xd;
+  float x8 = x4 * x4;
+  float p0 = fputil::multiply_add(x4, c1, c0);
+  float p1 = fputil::multiply_add(x4, c3, c2);
 
-  double x4 = xsq * xsq;
-  double c0 =
-      fputil::multiply_add(xsq, ERFF_COEFFS[idx][1], ERFF_COEFFS[idx][0]);
-  double c1 =
-      fputil::multiply_add(xsq, ERFF_COEFFS[idx][3], ERFF_COEFFS[idx][2]);
-  double c2 =
-      fputil::multiply_add(xsq, ERFF_COEFFS[idx][5], ERFF_COEFFS[idx][4]);
-  double c3 =
-      fputil::multiply_add(xsq, ERFF_COEFFS[idx][7], ERFF_COEFFS[idx][6]);
+  float result = xf * fputil::multiply_add(x8, p1, p0);
 
-  double x8 = x4 * x4;
-  double p0 = fputil::multiply_add(x4, c1, c0);
-  double p1 = fputil::multiply_add(x4, c3, c2);
+  // Clamp result to just inside [-1, 1]
+  if (LIBC_UNLIKELY(result > 1.0f))
+    return fputil::cast<float16>(1.0f - xf * 0x1.0p-28f);
+  else if (LIBC_UNLIKELY(result < -1.0f))
+    return fputil::cast<float16>(-1.0f - xf * 0x1.0p-28f);
 
-  return static_cast<float16>(xd * fputil::multiply_add(x8, p1, p0));
+  return fputil::cast<float16>(result);
 }
 
 } // namespace math



More information about the libc-commits mailing list