[libc-commits] [libc] [llvm] [libc][math] Implement half precision tgamma function (PR #192590)
via libc-commits
libc-commits at lists.llvm.org
Sun Apr 19 00:21:29 PDT 2026
https://github.com/AnonMiraj updated https://github.com/llvm/llvm-project/pull/192590
>From bd9d24caed88ae2034300980ccd92c10aa3dc4d2 Mon Sep 17 00:00:00 2001
From: Anonmiraj <ezzibrahimx at gmail.com>
Date: Fri, 17 Apr 2026 06:09:48 +0200
Subject: [PATCH 1/4] [libc][math] Implement half precision tgamma function
---
libc/config/gpu/amdgpu/entrypoints.txt | 2 +
libc/config/gpu/nvptx/entrypoints.txt | 2 +
libc/config/linux/aarch64/entrypoints.txt | 1 +
libc/config/linux/riscv/entrypoints.txt | 1 +
libc/config/linux/x86_64/entrypoints.txt | 1 +
libc/include/math.yaml | 7 +
libc/shared/math.h | 1 +
libc/shared/math/tgammaf16.h | 27 +++
libc/src/__support/math/CMakeLists.txt | 16 ++
libc/src/__support/math/tgammaf16.h | 166 ++++++++++++++++++
libc/src/math/CMakeLists.txt | 1 +
libc/src/math/generic/CMakeLists.txt | 12 ++
libc/src/math/generic/tgammaf16.cpp | 19 ++
libc/src/math/tgammaf16.h | 21 +++
libc/test/shared/CMakeLists.txt | 1 +
libc/test/shared/shared_math_test.cpp | 2 +
libc/test/src/math/CMakeLists.txt | 12 ++
libc/test/src/math/smoke/CMakeLists.txt | 11 ++
libc/test/src/math/smoke/tgammaf16_test.cpp | 89 ++++++++++
libc/test/src/math/tgammaf16_test.cpp | 42 +++++
libc/utils/MPFRWrapper/MPCommon.cpp | 6 +
libc/utils/MPFRWrapper/MPCommon.h | 1 +
libc/utils/MPFRWrapper/MPFRUtils.cpp | 2 +
libc/utils/MPFRWrapper/MPFRUtils.h | 1 +
.../llvm-project-overlay/libc/BUILD.bazel | 21 +++
25 files changed, 465 insertions(+)
create mode 100644 libc/shared/math/tgammaf16.h
create mode 100644 libc/src/__support/math/tgammaf16.h
create mode 100644 libc/src/math/generic/tgammaf16.cpp
create mode 100644 libc/src/math/tgammaf16.h
create mode 100644 libc/test/src/math/smoke/tgammaf16_test.cpp
create mode 100644 libc/test/src/math/tgammaf16_test.cpp
diff --git a/libc/config/gpu/amdgpu/entrypoints.txt b/libc/config/gpu/amdgpu/entrypoints.txt
index c76900a8371f7..d5275ead3abb1 100644
--- a/libc/config/gpu/amdgpu/entrypoints.txt
+++ b/libc/config/gpu/amdgpu/entrypoints.txt
@@ -509,6 +509,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.sqrtl
libc.src.math.tan
libc.src.math.tanf
+ libc.src.math.tgammaf16
libc.src.math.tanhf
libc.src.math.tanpif
libc.src.math.tgamma
@@ -629,6 +630,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
libc.src.math.sinpif16
libc.src.math.sqrtf16
libc.src.math.tanf16
+ libc.src.math.tgammaf16
libc.src.math.tanhf16
libc.src.math.tanpif16
libc.src.math.totalorderf16
diff --git a/libc/config/gpu/nvptx/entrypoints.txt b/libc/config/gpu/nvptx/entrypoints.txt
index 6538732f785f5..944c16c248dfe 100644
--- a/libc/config/gpu/nvptx/entrypoints.txt
+++ b/libc/config/gpu/nvptx/entrypoints.txt
@@ -510,6 +510,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.sqrtl
libc.src.math.tan
libc.src.math.tanf
+ libc.src.math.tgammaf16
libc.src.math.tanhf
libc.src.math.tanpif
libc.src.math.tgamma
@@ -631,6 +632,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
libc.src.math.sinpif16
libc.src.math.sqrtf16
libc.src.math.tanf16
+ libc.src.math.tgammaf16
libc.src.math.tanhf16
libc.src.math.tanpif16
libc.src.math.totalorderf16
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 6f7567cf3789f..443b15212e316 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -773,6 +773,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
libc.src.math.sqrtf16
libc.src.math.totalorderf16
libc.src.math.totalordermagf16
+ libc.src.math.tgammaf16
libc.src.math.truncf16
libc.src.math.ufromfpf16
libc.src.math.ufromfpxf16
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 1856d8edba7de..b67a0b48798e0 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -792,6 +792,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
libc.src.math.tanf16
libc.src.math.tanhf16
libc.src.math.tanpif16
+ libc.src.math.tgammaf16
libc.src.math.totalorderf16
libc.src.math.totalordermagf16
libc.src.math.truncf16
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index fe5991c695e25..e6376258a4053 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -846,6 +846,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
libc.src.math.tanf16
libc.src.math.tanhf16
libc.src.math.tanpif16
+ libc.src.math.tgammaf16
libc.src.math.totalorderf16
libc.src.math.totalordermagf16
libc.src.math.truncf16
diff --git a/libc/include/math.yaml b/libc/include/math.yaml
index 3984a665b234b..667419999f95a 100644
--- a/libc/include/math.yaml
+++ b/libc/include/math.yaml
@@ -2696,6 +2696,13 @@ functions:
arguments:
- type: _Float16
guard: LIBC_TYPES_HAS_FLOAT16
+ - name: tgammaf16
+ standards:
+ - stdc
+ return_type: _Float16
+ arguments:
+ - type: _Float16
+ guard: LIBC_TYPES_HAS_FLOAT16
- name: totalorder
standards:
- stdc
diff --git a/libc/shared/math.h b/libc/shared/math.h
index 83cd6196b146b..02560c3f3cf47 100644
--- a/libc/shared/math.h
+++ b/libc/shared/math.h
@@ -269,5 +269,6 @@
#include "math/tanhf16.h"
#include "math/tanpif.h"
#include "math/tanpif16.h"
+#include "math/tgammaf16.h"
#endif // LLVM_LIBC_SHARED_MATH_H
diff --git a/libc/shared/math/tgammaf16.h b/libc/shared/math/tgammaf16.h
new file mode 100644
index 0000000000000..1ed3ed739ea0a
--- /dev/null
+++ b/libc/shared/math/tgammaf16.h
@@ -0,0 +1,27 @@
+//===-- Shared tgammaf16 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_TGAMMAF16_H
+#define LLVM_LIBC_SHARED_MATH_TGAMMAF16_H
+
+#ifdef LIBC_TYPES_HAS_FLOAT16
+
+#include "shared/libc_common.h"
+#include "src/__support/math/tgammaf16.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace shared {
+
+using math::tgammaf16;
+
+} // namespace shared
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LIBC_TYPES_HAS_FLOAT16
+
+#endif // LLVM_LIBC_SHARED_MATH_TGAMMAF16_H
diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt
index d1c42afbaafe1..46f1feaab3ea5 100644
--- a/libc/src/__support/math/CMakeLists.txt
+++ b/libc/src/__support/math/CMakeLists.txt
@@ -3400,3 +3400,19 @@ add_header_library(
libc.src.__support.FPUtil.multiply_add
libc.src.__support.macros.optimization
)
+
+add_header_library(
+ tgammaf16
+ HDRS
+ tgammaf16.h
+ DEPENDS
+ libc.src.__support.CPP.bit
+ libc.src.__support.FPUtil.cast
+ libc.src.__support.FPUtil.fenv_impl
+ libc.src.__support.FPUtil.fp_bits
+ libc.src.__support.FPUtil.multiply_add
+ libc.src.__support.FPUtil.nearest_integer
+ libc.src.__support.FPUtil.nearest_integer_operations
+ libc.src.__support.macros.config
+ libc.src.__support.macros.optimization
+)
diff --git a/libc/src/__support/math/tgammaf16.h b/libc/src/__support/math/tgammaf16.h
new file mode 100644
index 0000000000000..9df373021ea29
--- /dev/null
+++ b/libc/src/__support/math/tgammaf16.h
@@ -0,0 +1,166 @@
+//===-- Implementation header for tgammaf16 ---------------------*- 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_TGAMMAF16_H
+#define LLVM_LIBC_SRC___SUPPORT_MATH_TGAMMAF16_H
+
+#include "include/llvm-libc-macros/float16-macros.h"
+
+#ifdef LIBC_TYPES_HAS_FLOAT16
+
+#include "src/__support/CPP/bit.h"
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/NearestIntegerOperations.h"
+#include "src/__support/FPUtil/cast.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/FPUtil/nearest_integer.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/optimization.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+namespace math {
+
+LIBC_INLINE constexpr bool is_integer(float16 x) {
+ using FPBits = fputil::FPBits<float16>;
+ FPBits xbits(x);
+ uint16_t x_u = xbits.uintval();
+ unsigned x_e = static_cast<unsigned>(xbits.get_biased_exponent());
+ unsigned lsb = static_cast<unsigned>(
+ cpp::countr_zero(static_cast<uint16_t>(x_u | FPBits::EXP_MASK)));
+ constexpr unsigned UNIT_EXPONENT =
+ static_cast<unsigned>(FPBits::EXP_BIAS + FPBits::FRACTION_LEN);
+ return x_e + lsb >= UNIT_EXPONENT;
+}
+
+LIBC_INLINE float16 tgammaf16(float16 x) {
+ using FPBits = typename fputil::FPBits<float16>;
+ FPBits xbits(x);
+ uint16_t x_abs = xbits.abs().uintval();
+
+ if (LIBC_UNLIKELY(x_abs >= 0x7c00U)) {
+ if (x_abs == 0x7c00U) {
+ // tgamma(-Inf) = NaN, tgamma(+Inf) = +Inf
+ if (xbits.is_neg()) {
+ fputil::raise_except_if_required(FE_INVALID);
+ fputil::set_errno_if_required(EDOM);
+ return FPBits::quiet_nan().get_val();
+ }
+ return x;
+ }
+ if (xbits.is_signaling_nan()) {
+ fputil::raise_except_if_required(FE_INVALID);
+ return FPBits::quiet_nan().get_val();
+ }
+ return x;
+ }
+
+ // -+0 pole error
+ if (LIBC_UNLIKELY(x_abs == 0)) {
+ fputil::raise_except_if_required(FE_DIVBYZERO);
+ fputil::set_errno_if_required(ERANGE);
+ return FPBits::inf(xbits.sign()).get_val();
+ }
+
+ double xd = fputil::cast<double>(x);
+
+ // |x| <= 2^-16
+ if (LIBC_UNLIKELY(x_abs <= 0x0100U)) {
+ double d = (0x1.fa658c23b1578p-1 - 0x1.d0a118f324b63p-1 * xd) * xd -
+ 0x1.2788cfc6fb619p-1;
+ double f = 1.0 / xd + d;
+ float16 result = fputil::cast<float16>(f);
+ if (LIBC_UNLIKELY(FPBits(result).is_inf())) {
+ fputil::raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
+ fputil::set_errno_if_required(ERANGE);
+ } else {
+ fputil::raise_except_if_required(FE_INEXACT);
+ }
+ return result;
+ }
+
+ // |x| >= 9.2265625 overflows float16
+ if (LIBC_UNLIKELY(!xbits.is_neg() && x_abs >= 0x489DU)) {
+ fputil::raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
+ fputil::set_errno_if_required(ERANGE);
+ return fputil::cast<float16>(fputil::FPBits<float>::max_normal().get_val());
+ }
+
+ // x is a non-zero integer
+ if (LIBC_UNLIKELY(is_integer(x))) {
+ int32_t k = static_cast<int32_t>(fputil::floor(xd));
+ if (xbits.is_neg()) {
+ fputil::raise_except_if_required(FE_INVALID);
+ fputil::set_errno_if_required(EDOM);
+ return FPBits::quiet_nan().get_val();
+ }
+ // gamma(n) = (n-1)! for n = 1..9 (n >= 10 overflows float16)
+ static constexpr float16 FACTORIAL[9] = {1.0, 1.0, 2.0, 6.0, 24.0,
+ 120.0, 720.0, 5040.0, 40320.0};
+ return FACTORIAL[k - 1];
+ }
+
+ // |x| < -16
+ if (LIBC_UNLIKELY(x_abs > 0x4C00U)) {
+ fputil::set_errno_if_required(ERANGE);
+ float res =
+ (static_cast<int32_t>(fputil::floor(x)) & 1) ? -0x1p-127f : 0x1p-127f;
+
+ return fputil::cast<float16>(res);
+ }
+
+ double m = xd - 0x1.7p+1;
+ double i = fputil::nearest_integer(m);
+ double step = fputil::FPBits<double>(i).is_neg() ? -1.0 : 1.0;
+ int32_t jm = static_cast<int32_t>(i < 0.0 ? -i : i);
+
+ double d = m - i;
+
+ // Polynomial approximating tgamma(d + 2.875) on [-0.5, 0.5] generated by
+ // Sollya with: > P = fpminimax(tgamma(x + 2.875),
+ // [|0,1,2,3,4,5,6,7|], [|D...|], [-0.5, 0.5]);
+
+ static constexpr double COEFFS[8] = {
+ 0x1.c9a76b7de6f60p+0, 0x1.8f2754874fe83p+0, 0x1.0d11cba0c229fp+0,
+ 0x1.e1f47c012064dp-2, 0x1.828b9f2afb74fp-3, 0x1.e1cc49d830795p-5,
+ 0x1.2c9af3e686836p-6, 0x1.1bf60c7e311f6p-8};
+ double d2 = d * d, d4 = d2 * d2;
+ double p01 = fputil::multiply_add(d, COEFFS[1], COEFFS[0]);
+ double p23 = fputil::multiply_add(d, COEFFS[3], COEFFS[2]);
+ double p45 = fputil::multiply_add(d, COEFFS[5], COEFFS[4]);
+ double p67 = fputil::multiply_add(d, COEFFS[7], COEFFS[6]);
+ double p03 = fputil::multiply_add(d2, p23, p01);
+ double p47 = fputil::multiply_add(d2, p67, p45);
+ double f = fputil::multiply_add(d4, p47, p03);
+
+ // Reduce to the base interval by multiplying the appropriate
+ // falling/rising factorial then invert for the negative-argument case
+ double z = xd;
+ double w = 1.0;
+ if (jm) {
+ z -= 0.5 + step * 0.5;
+ w = z;
+ for (int32_t j = jm - 1; j; j--) {
+ z -= step;
+ w *= z;
+ }
+ }
+ if (i <= -0.5)
+ w = 1.0 / w;
+
+ return fputil::cast<float16>(f * w);
+}
+
+} // namespace math
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LIBC_TYPES_HAS_FLOAT16
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MATH_TGAMMAF16_H
diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index b53817e2a1729..bc726991af15e 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -595,6 +595,7 @@ add_math_entrypoint_object(tanpif16)
add_math_entrypoint_object(tgamma)
add_math_entrypoint_object(tgammaf)
+add_math_entrypoint_object(tgammaf16)
add_math_entrypoint_object(lgamma)
add_math_entrypoint_object(lgamma_r)
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index 740b78418492b..9a78d9760a5d0 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -5278,3 +5278,15 @@ add_entrypoint_object(
DEPENDS
libc.src.__support.math.bf16subf128
)
+
+add_entrypoint_object(
+ tgammaf16
+ SRCS
+ tgammaf16.cpp
+ HDRS
+ ../tgammaf16.h
+ DEPENDS
+ libc.src.__support.common
+ libc.src.__support.math.tgammaf16
+ libc.src.errno.errno
+)
diff --git a/libc/src/math/generic/tgammaf16.cpp b/libc/src/math/generic/tgammaf16.cpp
new file mode 100644
index 0000000000000..01d71bc700a3c
--- /dev/null
+++ b/libc/src/math/generic/tgammaf16.cpp
@@ -0,0 +1,19 @@
+//===-- Implementation of tgammaf16 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/tgammaf16.h"
+#include "src/__support/common.h"
+#include "src/__support/math/tgammaf16.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(float16, tgammaf16, (float16 x)) {
+ return math::tgammaf16(x);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/math/tgammaf16.h b/libc/src/math/tgammaf16.h
new file mode 100644
index 0000000000000..583f65d8bedfe
--- /dev/null
+++ b/libc/src/math/tgammaf16.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for tgammaf16 ---------------------*- 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_TGAMMAF16_H
+#define LLVM_LIBC_SRC_MATH_TGAMMAF16_H
+
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+float16 tgammaf16(float16 x);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_MATH_TGAMMAF16_H
diff --git a/libc/test/shared/CMakeLists.txt b/libc/test/shared/CMakeLists.txt
index 3289321f25dd4..4fa95d92764b9 100644
--- a/libc/test/shared/CMakeLists.txt
+++ b/libc/test/shared/CMakeLists.txt
@@ -267,6 +267,7 @@ add_fp_unittest(
libc.src.__support.math.tanhf16
libc.src.__support.math.tanpif
libc.src.__support.math.tanpif16
+ libc.src.__support.math.tgammaf16
)
add_fp_unittest(
diff --git a/libc/test/shared/shared_math_test.cpp b/libc/test/shared/shared_math_test.cpp
index 5d83ce22af4ab..3efe2ee970f1c 100644
--- a/libc/test/shared/shared_math_test.cpp
+++ b/libc/test/shared/shared_math_test.cpp
@@ -67,6 +67,8 @@ TEST(LlvmLibcSharedMathTest, AllFloat16) {
EXPECT_FP_EQ(0.0f16, LIBC_NAMESPACE::shared::tanhf16(0.0f16));
EXPECT_FP_EQ(0.0f16, LIBC_NAMESPACE::shared::tanpif16(0.0f16));
+ EXPECT_FP_EQ(1.0f16, LIBC_NAMESPACE::shared::tgammaf16(1.0f16));
+
float16 canonicalizef16_cx = 0.0f16;
float16 canonicalizef16_x = 0.0f16;
EXPECT_EQ(0, LIBC_NAMESPACE::shared::canonicalizef16(&canonicalizef16_cx,
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 4213c11eca515..9b643878a76e8 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -3633,3 +3633,15 @@ if(NOT LLVM_LIBC_FULL_BUILD)
add_subdirectory(exhaustive)
add_subdirectory(performance_testing)
endif()
+
+add_fp_unittest(
+ tgammaf16_test
+ NEED_MPFR
+ SUITE
+ libc-math-unittests
+ SRCS
+ tgammaf16_test.cpp
+ DEPENDS
+ libc.src.__support.FPUtil.fp_bits
+ libc.src.math.tgammaf16
+)
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index 28b85b1a25bbd..e77ff61991d92 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -6686,3 +6686,14 @@ add_fp_unittest(
libc.src.__support.FPUtil.bfloat16
libc.src.__support.macros.properties.os
)
+
+add_fp_unittest(
+ tgammaf16_test
+ SUITE
+ libc-math-smoke-tests
+ SRCS
+ tgammaf16_test.cpp
+ DEPENDS
+ libc.src.__support.FPUtil.fp_bits
+ libc.src.math.tgammaf16
+)
diff --git a/libc/test/src/math/smoke/tgammaf16_test.cpp b/libc/test/src/math/smoke/tgammaf16_test.cpp
new file mode 100644
index 0000000000000..50cf8554e7616
--- /dev/null
+++ b/libc/test/src/math/smoke/tgammaf16_test.cpp
@@ -0,0 +1,89 @@
+//===-- Unittests for tgammaf16 -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "src/math/tgammaf16.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcTgammaf16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
+
+TEST_F(LlvmLibcTgammaf16Test, SpecialNumbers) {
+ // sNaN -> qNaN + FE_INVALID
+ EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::tgammaf16(sNaN),
+ FE_INVALID);
+ EXPECT_MATH_ERRNO(0);
+
+ // qNaN -> qNaN
+ EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::tgammaf16(aNaN));
+
+ // +Inf -> +Inf
+ EXPECT_FP_EQ_ALL_ROUNDING(inf, LIBC_NAMESPACE::tgammaf16(inf));
+
+ // -Inf -> NaN + FE_INVALID (domain error)
+ EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::tgammaf16(neg_inf),
+ FE_INVALID);
+ EXPECT_MATH_ERRNO(EDOM);
+
+ // +0 -> +Inf + FE_DIVBYZERO (pole error)
+ EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::tgammaf16(zero),
+ FE_DIVBYZERO);
+ EXPECT_MATH_ERRNO(ERANGE);
+
+ // -0 -> -Inf + FE_DIVBYZERO (pole error)
+ EXPECT_FP_EQ_WITH_EXCEPTION(neg_inf, LIBC_NAMESPACE::tgammaf16(neg_zero),
+ FE_DIVBYZERO);
+ EXPECT_MATH_ERRNO(ERANGE);
+}
+
+TEST_F(LlvmLibcTgammaf16Test, PositiveIntegers) {
+ EXPECT_FP_EQ_ALL_ROUNDING(1.0f16, LIBC_NAMESPACE::tgammaf16(1.0f16));
+ EXPECT_FP_EQ_ALL_ROUNDING(1.0f16, LIBC_NAMESPACE::tgammaf16(2.0f16));
+ EXPECT_FP_EQ_ALL_ROUNDING(2.0f16, LIBC_NAMESPACE::tgammaf16(3.0f16));
+ EXPECT_FP_EQ_ALL_ROUNDING(6.0f16, LIBC_NAMESPACE::tgammaf16(4.0f16));
+ EXPECT_FP_EQ_ALL_ROUNDING(24.0f16, LIBC_NAMESPACE::tgammaf16(5.0f16));
+}
+
+TEST_F(LlvmLibcTgammaf16Test, NegativeIntegers) {
+ // tgamma of negative integer -> NaN + FE_INVALID (domain error)
+ EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::tgammaf16(-1.0f16),
+ FE_INVALID);
+ EXPECT_MATH_ERRNO(EDOM);
+
+ EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::tgammaf16(-2.0f16),
+ FE_INVALID);
+ EXPECT_MATH_ERRNO(EDOM);
+}
+
+TEST_F(LlvmLibcTgammaf16Test, Overflow) {
+ // x >= 9.2265625 overflows float16
+ EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::tgammaf16(9.25f16),
+ FE_OVERFLOW | FE_INEXACT);
+ EXPECT_MATH_ERRNO(ERANGE);
+}
+
+#ifdef LIBC_TEST_FTZ_DAZ
+
+using namespace LIBC_NAMESPACE::testing;
+
+TEST_F(LlvmLibcTgammaf16Test, FTZMode) {
+ ModifyMXCSR mxcsr(FTZ);
+ EXPECT_FP_EQ(inf, LIBC_NAMESPACE::tgammaf16(min_denormal));
+}
+
+TEST_F(LlvmLibcTgammaf16Test, DAZMode) {
+ ModifyMXCSR mxcsr(DAZ);
+ EXPECT_FP_EQ(inf, LIBC_NAMESPACE::tgammaf16(min_denormal));
+}
+
+TEST_F(LlvmLibcTgammaf16Test, FTZDAZMode) {
+ ModifyMXCSR mxcsr(FTZ | DAZ);
+ EXPECT_FP_EQ(inf, LIBC_NAMESPACE::tgammaf16(min_denormal));
+}
+
+#endif
diff --git a/libc/test/src/math/tgammaf16_test.cpp b/libc/test/src/math/tgammaf16_test.cpp
new file mode 100644
index 0000000000000..9fd18a256c252
--- /dev/null
+++ b/libc/test/src/math/tgammaf16_test.cpp
@@ -0,0 +1,42 @@
+//===-- Unittests for tgammaf16 -------------------------------------------===//
+//
+// 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/tgammaf16.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+
+using LlvmLibcTgammaf16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
+
+namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
+
+// Range: ]0, Inf[
+static constexpr uint16_t POS_START = 0x0001U;
+static constexpr uint16_t POS_STOP = 0x7bffU;
+
+// Range: ]-Inf, 0[
+static constexpr uint16_t NEG_START = 0x8001U;
+static constexpr uint16_t NEG_STOP = 0xfbffU;
+
+TEST_F(LlvmLibcTgammaf16Test, PositiveRange) {
+ for (uint16_t v = POS_START; v <= POS_STOP; ++v) {
+ float16 x = FPBits(v).get_val();
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Tgamma, x,
+ LIBC_NAMESPACE::tgammaf16(x), 0.5);
+ }
+}
+
+TEST_F(LlvmLibcTgammaf16Test, NegativeRange) {
+ for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) {
+ float16 x = FPBits(v).get_val();
+ if (FPBits(v).is_nan())
+ continue;
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Tgamma, x,
+ LIBC_NAMESPACE::tgammaf16(x), 0.5);
+ }
+}
diff --git a/libc/utils/MPFRWrapper/MPCommon.cpp b/libc/utils/MPFRWrapper/MPCommon.cpp
index bdd8286148641..b2ade7ab34878 100644
--- a/libc/utils/MPFRWrapper/MPCommon.cpp
+++ b/libc/utils/MPFRWrapper/MPCommon.cpp
@@ -575,6 +575,12 @@ MPFRNumber MPFRNumber::tanpi() const {
#endif
}
+MPFRNumber MPFRNumber::tgamma() const {
+ MPFRNumber result(*this);
+ mpfr_gamma(result.value, value, mpfr_rounding);
+ return result;
+}
+
MPFRNumber MPFRNumber::trunc() const {
MPFRNumber result(*this);
mpfr_trunc(result.value, value);
diff --git a/libc/utils/MPFRWrapper/MPCommon.h b/libc/utils/MPFRWrapper/MPCommon.h
index 935a2614968a2..96149487170a5 100644
--- a/libc/utils/MPFRWrapper/MPCommon.h
+++ b/libc/utils/MPFRWrapper/MPCommon.h
@@ -237,6 +237,7 @@ class MPFRNumber {
MPFRNumber tan() const;
MPFRNumber tanh() const;
MPFRNumber tanpi() const;
+ MPFRNumber tgamma() const;
MPFRNumber trunc() const;
MPFRNumber fma(const MPFRNumber &b, const MPFRNumber &c);
MPFRNumber mul(const MPFRNumber &b);
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp
index 356764302bda8..2fadaf4bc8388 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp
@@ -113,6 +113,8 @@ unary_operation(Operation op, InputType input, unsigned int precision,
return mpfrInput.tanh();
case Operation::Tanpi:
return mpfrInput.tanpi();
+ case Operation::Tgamma:
+ return mpfrInput.tgamma();
case Operation::Trunc:
return mpfrInput.trunc();
default:
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.h b/libc/utils/MPFRWrapper/MPFRUtils.h
index 6c1b15598b0f2..d265f4ae2cad4 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.h
+++ b/libc/utils/MPFRWrapper/MPFRUtils.h
@@ -67,6 +67,7 @@ enum class Operation : int {
Tan,
Tanh,
Tanpi,
+ Tgamma,
Trunc,
EndUnaryOperationsSingleOutput,
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index eb4a97b457ca8..1c20e4ffa8865 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -6377,6 +6377,22 @@ libc_support_library(
],
)
+libc_support_library(
+ name = "__support_math_tgammaf16",
+ hdrs = ["src/__support/math/tgammaf16.h"],
+ deps = [
+ ":__support_cpp_bit",
+ ":__support_fputil_cast",
+ ":__support_fputil_fenv_impl",
+ ":__support_fputil_fp_bits",
+ ":__support_fputil_multiply_add",
+ ":__support_fputil_nearest_integer",
+ ":__support_fputil_nearest_integer_operations",
+ ":__support_macros_config",
+ ":__support_macros_optimization",
+ ],
+)
+
############################### complex targets ################################
libc_function(
@@ -8854,6 +8870,11 @@ libc_math_function(
additional_deps = [":__support_math_tanpif16"],
)
+libc_math_function(
+ name = "tgammaf16",
+ additional_deps = [":__support_math_tgammaf16"],
+)
+
libc_math_function(name = "totalorder")
libc_math_function(name = "totalorderf")
>From 7eace73c8c51c617f9414e2101a3390ddbed4d21 Mon Sep 17 00:00:00 2001
From: Anonmiraj <ezzibrahimx at gmail.com>
Date: Fri, 17 Apr 2026 15:12:20 +0200
Subject: [PATCH 2/4] fix nits
---
libc/config/baremetal/aarch64/entrypoints.txt | 1 +
libc/config/baremetal/arm/entrypoints.txt | 1 +
libc/config/baremetal/riscv/entrypoints.txt | 1 +
libc/src/__support/math/CMakeLists.txt | 2 ++
libc/src/__support/math/tgammaf16.h | 1 +
libc/src/math/generic/CMakeLists.txt | 1 +
libc/src/math/generic/tgammaf16.cpp | 1 +
libc/test/src/math/CMakeLists.txt | 18 +++++++++---------
libc/test/src/math/smoke/CMakeLists.txt | 2 +-
9 files changed, 18 insertions(+), 10 deletions(-)
diff --git a/libc/config/baremetal/aarch64/entrypoints.txt b/libc/config/baremetal/aarch64/entrypoints.txt
index 452abd985b3a5..eaa94c81147ac 100644
--- a/libc/config/baremetal/aarch64/entrypoints.txt
+++ b/libc/config/baremetal/aarch64/entrypoints.txt
@@ -691,6 +691,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
libc.src.math.tanpif16
libc.src.math.totalorderf16
libc.src.math.totalordermagf16
+ libc.src.math.tgammaf16
libc.src.math.truncf16
libc.src.math.ufromfpf16
libc.src.math.ufromfpxf16
diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt
index 9c0e71b2beec0..7acbf343aff2b 100644
--- a/libc/config/baremetal/arm/entrypoints.txt
+++ b/libc/config/baremetal/arm/entrypoints.txt
@@ -700,6 +700,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
libc.src.math.tanpif16
libc.src.math.totalorderf16
libc.src.math.totalordermagf16
+ libc.src.math.tgammaf16
libc.src.math.truncf16
libc.src.math.ufromfpf16
libc.src.math.ufromfpxf16
diff --git a/libc/config/baremetal/riscv/entrypoints.txt b/libc/config/baremetal/riscv/entrypoints.txt
index 5c88bc4ae1fb7..2d50e43665662 100644
--- a/libc/config/baremetal/riscv/entrypoints.txt
+++ b/libc/config/baremetal/riscv/entrypoints.txt
@@ -697,6 +697,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
libc.src.math.tanpif16
libc.src.math.totalorderf16
libc.src.math.totalordermagf16
+ libc.src.math.tgammaf16
libc.src.math.truncf16
libc.src.math.ufromfpf16
libc.src.math.ufromfpxf16
diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt
index 46f1feaab3ea5..a78075de8d178 100644
--- a/libc/src/__support/math/CMakeLists.txt
+++ b/libc/src/__support/math/CMakeLists.txt
@@ -3406,6 +3406,7 @@ add_header_library(
HDRS
tgammaf16.h
DEPENDS
+ libc.include.llvm-libc-macros.float16_macros
libc.src.__support.CPP.bit
libc.src.__support.FPUtil.cast
libc.src.__support.FPUtil.fenv_impl
@@ -3415,4 +3416,5 @@ add_header_library(
libc.src.__support.FPUtil.nearest_integer_operations
libc.src.__support.macros.config
libc.src.__support.macros.optimization
+ libc.src.__support.macros.properties.types
)
diff --git a/libc/src/__support/math/tgammaf16.h b/libc/src/__support/math/tgammaf16.h
index 9df373021ea29..f0d956c9f0ae2 100644
--- a/libc/src/__support/math/tgammaf16.h
+++ b/libc/src/__support/math/tgammaf16.h
@@ -22,6 +22,7 @@
#include "src/__support/FPUtil/nearest_integer.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h"
+#include "src/__support/macros/properties/types.h"
namespace LIBC_NAMESPACE_DECL {
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index 9a78d9760a5d0..51c28bf32057b 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -5287,6 +5287,7 @@ add_entrypoint_object(
../tgammaf16.h
DEPENDS
libc.src.__support.common
+ libc.src.__support.macros.config
libc.src.__support.math.tgammaf16
libc.src.errno.errno
)
diff --git a/libc/src/math/generic/tgammaf16.cpp b/libc/src/math/generic/tgammaf16.cpp
index 01d71bc700a3c..1b15d41f20e36 100644
--- a/libc/src/math/generic/tgammaf16.cpp
+++ b/libc/src/math/generic/tgammaf16.cpp
@@ -8,6 +8,7 @@
#include "src/math/tgammaf16.h"
#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
#include "src/__support/math/tgammaf16.h"
namespace LIBC_NAMESPACE_DECL {
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 9b643878a76e8..130cd07bc3024 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -3626,14 +3626,6 @@ add_fp_unittest(
libc.src.__support.FPUtil.bfloat16
)
-add_subdirectory(generic)
-add_subdirectory(smoke)
-
-if(NOT LLVM_LIBC_FULL_BUILD)
- add_subdirectory(exhaustive)
- add_subdirectory(performance_testing)
-endif()
-
add_fp_unittest(
tgammaf16_test
NEED_MPFR
@@ -3642,6 +3634,14 @@ add_fp_unittest(
SRCS
tgammaf16_test.cpp
DEPENDS
- libc.src.__support.FPUtil.fp_bits
libc.src.math.tgammaf16
)
+
+add_subdirectory(generic)
+add_subdirectory(smoke)
+
+if(NOT LLVM_LIBC_FULL_BUILD)
+ add_subdirectory(exhaustive)
+ add_subdirectory(performance_testing)
+endif()
+
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index e77ff61991d92..20ac8c09343cd 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -6694,6 +6694,6 @@ add_fp_unittest(
SRCS
tgammaf16_test.cpp
DEPENDS
- libc.src.__support.FPUtil.fp_bits
+ libc.hdr.errno_macros
libc.src.math.tgammaf16
)
>From a9a6f8072164fd1d7090e8772381e2637b7e9db9 Mon Sep 17 00:00:00 2001
From: Anonmiraj <ezzibrahimx at gmail.com>
Date: Fri, 17 Apr 2026 15:22:14 +0200
Subject: [PATCH 3/4] fix bazel build
---
utils/bazel/llvm-project-overlay/libc/BUILD.bazel | 2 ++
1 file changed, 2 insertions(+)
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index 1c20e4ffa8865..9316118f6be26 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -6390,6 +6390,8 @@ libc_support_library(
":__support_fputil_nearest_integer_operations",
":__support_macros_config",
":__support_macros_optimization",
+ ":__support_macros_properties_types",
+ ":llvm_libc_macros_float16_macros",
],
)
>From 60e163dd8faac869a0fd524cee1db368dc84f7d0 Mon Sep 17 00:00:00 2001
From: Anonmiraj <ezzibrahimx at gmail.com>
Date: Sun, 19 Apr 2026 09:21:13 +0200
Subject: [PATCH 4/4] fix nits
---
libc/src/__support/math/tgammaf16.h | 35 ++++++++++++++---------------
1 file changed, 17 insertions(+), 18 deletions(-)
diff --git a/libc/src/__support/math/tgammaf16.h b/libc/src/__support/math/tgammaf16.h
index f0d956c9f0ae2..67aa60e6cc93f 100644
--- a/libc/src/__support/math/tgammaf16.h
+++ b/libc/src/__support/math/tgammaf16.h
@@ -28,22 +28,21 @@ namespace LIBC_NAMESPACE_DECL {
namespace math {
-LIBC_INLINE constexpr bool is_integer(float16 x) {
- using FPBits = fputil::FPBits<float16>;
- FPBits xbits(x);
- uint16_t x_u = xbits.uintval();
- unsigned x_e = static_cast<unsigned>(xbits.get_biased_exponent());
- unsigned lsb = static_cast<unsigned>(
- cpp::countr_zero(static_cast<uint16_t>(x_u | FPBits::EXP_MASK)));
- constexpr unsigned UNIT_EXPONENT =
- static_cast<unsigned>(FPBits::EXP_BIAS + FPBits::FRACTION_LEN);
- return x_e + lsb >= UNIT_EXPONENT;
-}
-
LIBC_INLINE float16 tgammaf16(float16 x) {
using FPBits = typename fputil::FPBits<float16>;
FPBits xbits(x);
uint16_t x_abs = xbits.abs().uintval();
+ auto is_integer = [](float16 x) {
+ using FPBits = fputil::FPBits<float16>;
+ FPBits xbits(x);
+ uint16_t x_u = xbits.uintval();
+ unsigned x_e = static_cast<unsigned>(xbits.get_biased_exponent());
+ unsigned lsb = static_cast<unsigned>(
+ cpp::countr_zero(static_cast<uint16_t>(x_u | FPBits::EXP_MASK)));
+ constexpr unsigned UNIT_EXPONENT =
+ static_cast<unsigned>(FPBits::EXP_BIAS + FPBits::FRACTION_LEN);
+ return x_e + lsb >= UNIT_EXPONENT;
+ };
if (LIBC_UNLIKELY(x_abs >= 0x7c00U)) {
if (x_abs == 0x7c00U) {
@@ -102,8 +101,8 @@ LIBC_INLINE float16 tgammaf16(float16 x) {
return FPBits::quiet_nan().get_val();
}
// gamma(n) = (n-1)! for n = 1..9 (n >= 10 overflows float16)
- static constexpr float16 FACTORIAL[9] = {1.0, 1.0, 2.0, 6.0, 24.0,
- 120.0, 720.0, 5040.0, 40320.0};
+ constexpr float16 FACTORIAL[9] = {1.0, 1.0, 2.0, 6.0, 24.0,
+ 120.0, 720.0, 5040.0, 40320.0};
return FACTORIAL[k - 1];
}
@@ -127,10 +126,10 @@ LIBC_INLINE float16 tgammaf16(float16 x) {
// Sollya with: > P = fpminimax(tgamma(x + 2.875),
// [|0,1,2,3,4,5,6,7|], [|D...|], [-0.5, 0.5]);
- static constexpr double COEFFS[8] = {
- 0x1.c9a76b7de6f60p+0, 0x1.8f2754874fe83p+0, 0x1.0d11cba0c229fp+0,
- 0x1.e1f47c012064dp-2, 0x1.828b9f2afb74fp-3, 0x1.e1cc49d830795p-5,
- 0x1.2c9af3e686836p-6, 0x1.1bf60c7e311f6p-8};
+ constexpr double COEFFS[8] = {0x1.c9a76b7de6f60p+0, 0x1.8f2754874fe83p+0,
+ 0x1.0d11cba0c229fp+0, 0x1.e1f47c012064dp-2,
+ 0x1.828b9f2afb74fp-3, 0x1.e1cc49d830795p-5,
+ 0x1.2c9af3e686836p-6, 0x1.1bf60c7e311f6p-8};
double d2 = d * d, d4 = d2 * d2;
double p01 = fputil::multiply_add(d, COEFFS[1], COEFFS[0]);
double p23 = fputil::multiply_add(d, COEFFS[3], COEFFS[2]);
More information about the libc-commits
mailing list