[libc-commits] [libc] [libc][math][C23] add software float16 support (PR #184283)
via libc-commits
libc-commits at lists.llvm.org
Sat Aug 1 09:22:19 PDT 2026
https://github.com/AnonMiraj updated https://github.com/llvm/llvm-project/pull/184283
>From 353115fbd88922ee2623da5033ffc9779f4fca59 Mon Sep 17 00:00:00 2001
From: Anonmiraj <ezzibrahimx at gmail.com>
Date: Tue, 3 Mar 2026 02:42:07 +0200
Subject: [PATCH] [libc][math][C23] add software float16 support
---
libc/config/linux/arm/entrypoints.txt | 5 +
.../include/llvm-libc-macros/float16-macros.h | 13 +-
libc/src/__support/FPUtil/BasicOperations.h | 4 +
libc/src/__support/FPUtil/CMakeLists.txt | 18 +++
libc/src/__support/FPUtil/Float16.h | 127 ++++++++++++++++++
libc/src/__support/macros/properties/types.h | 12 ++
libc/src/__support/math/CMakeLists.txt | 1 +
libc/src/__support/math/fabsf16.h | 4 +-
libc/src/math/generic/fabsf16.cpp | 1 -
libc/test/src/math/exhaustive/CMakeLists.txt | 17 +++
.../src/math/exhaustive/exhaustive_test.h | 1 +
.../test/src/math/exhaustive/float16_test.cpp | 67 +++++++++
libc/test/src/math/smoke/CMakeLists.txt | 1 +
libc/test/src/math/smoke/fabsf16_test.cpp | 2 +-
libc/utils/MPFRWrapper/CMakeLists.txt | 2 +
libc/utils/MPFRWrapper/MPCommon.cpp | 7 +-
libc/utils/MPFRWrapper/MPFRUtils.cpp | 1 +
17 files changed, 276 insertions(+), 7 deletions(-)
create mode 100644 libc/src/__support/FPUtil/Float16.h
create mode 100644 libc/test/src/math/exhaustive/float16_test.cpp
diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt
index 1986d6a5347dc..608b50843b94a 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -506,6 +506,11 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.ufromfpxl
)
+list(APPEND TARGET_LIBM_ENTRYPOINTS
+ # float16 entrypoints
+ libc.src.math.fabsf16
+)
+
list(APPEND TARGET_LIBM_ENTRYPOINTS
# bfloat16 entrypoints
libc.src.math.atanbf16
diff --git a/libc/include/llvm-libc-macros/float16-macros.h b/libc/include/llvm-libc-macros/float16-macros.h
index 528c7f016f873..0b40a1b85636c 100644
--- a/libc/include/llvm-libc-macros/float16-macros.h
+++ b/libc/include/llvm-libc-macros/float16-macros.h
@@ -11,11 +11,18 @@
#include "../llvm-libc-types/float128.h"
-#if defined(__FLT16_MANT_DIG__) && \
+#if defined(__arm__) && defined(_M_ARM)
+#define LIBC_USE_SOFT_FLOAT16
+#endif
+
+#ifdef LIBC_USE_SOFT_FLOAT16
+#define LIBC_TYPES_HAS_FLOAT16
+#endif
+
+#if !defined(LIBC_TYPES_HAS_FLOAT16) && defined(__FLT16_MANT_DIG__) && \
(!defined(__GNUC__) || __GNUC__ >= 13 || \
(defined(__clang__) && __clang_major__ >= 12)) && \
- !defined(__arm__) && !defined(_M_ARM) && !defined(__riscv) && \
- !defined(_WIN32)
+ !defined(__riscv) && !defined(_WIN32)
#define LIBC_TYPES_HAS_FLOAT16
// TODO: This would no longer be required if HdrGen let us guard function
diff --git a/libc/src/__support/FPUtil/BasicOperations.h b/libc/src/__support/FPUtil/BasicOperations.h
index 5b81b921296a0..747d089f3da73 100644
--- a/libc/src/__support/FPUtil/BasicOperations.h
+++ b/libc/src/__support/FPUtil/BasicOperations.h
@@ -53,6 +53,7 @@ max(T x, T y) {
}
#ifdef LIBC_TYPES_HAS_FLOAT16
+#if !defined(LIBC_USE_SOFT_FLOAT16)
#if defined(__LIBC_USE_BUILTIN_FMAXF16_FMINF16)
template <> LIBC_INLINE constexpr float16 max(float16 x, float16 y) {
if (cpp::is_constant_evaluated())
@@ -69,6 +70,7 @@ template <> LIBC_INLINE constexpr float16 max(float16 x, float16 y) {
return ((xi > yi) != (xi < 0 && yi < 0)) ? x : y;
}
#endif
+#endif // !LIBC_USE_SOFT_FLOAT16
#endif // LIBC_TYPES_HAS_FLOAT16
#if defined(__LIBC_USE_BUILTIN_FMAX_FMIN) && !defined(LIBC_TARGET_ARCH_IS_X86)
@@ -106,6 +108,7 @@ min(T x, T y) {
}
#ifdef LIBC_TYPES_HAS_FLOAT16
+#if !defined(LIBC_USE_SOFT_FLOAT16)
#if defined(__LIBC_USE_BUILTIN_FMAXF16_FMINF16)
template <> LIBC_INLINE constexpr float16 min(float16 x, float16 y) {
if (cpp::is_constant_evaluated())
@@ -122,6 +125,7 @@ template <> LIBC_INLINE constexpr float16 min(float16 x, float16 y) {
return ((xi < yi) != (xi < 0 && yi < 0)) ? x : y;
}
#endif
+#endif // !LIBC_USE_SOFT_FLOAT16
#endif // LIBC_TYPES_HAS_FLOAT16
#if defined(__LIBC_USE_BUILTIN_FMAX_FMIN) && !defined(LIBC_TARGET_ARCH_IS_X86)
diff --git a/libc/src/__support/FPUtil/CMakeLists.txt b/libc/src/__support/FPUtil/CMakeLists.txt
index 4673ef794c80b..2f86adc9b745e 100644
--- a/libc/src/__support/FPUtil/CMakeLists.txt
+++ b/libc/src/__support/FPUtil/CMakeLists.txt
@@ -43,6 +43,24 @@ add_header_library(
libc.src.__support.uint128
)
+add_header_library(
+ Float16
+ HDRS
+ Float16.h
+ DEPENDS
+ .cast
+ .comparison_operations
+ .dyadic_float
+ libc.hdr.stdint_proxy
+ libc.src.__support.CPP.bit
+ libc.src.__support.CPP.type_traits
+ libc.src.__support.FPUtil.generic.add_sub
+ libc.src.__support.FPUtil.generic.div
+ libc.src.__support.FPUtil.generic.mul
+ libc.src.__support.macros.config
+ libc.src.__support.macros.properties.types
+)
+
add_header_library(
fpbits_str
HDRS
diff --git a/libc/src/__support/FPUtil/Float16.h b/libc/src/__support/FPUtil/Float16.h
new file mode 100644
index 0000000000000..5387e6a7c1c7a
--- /dev/null
+++ b/libc/src/__support/FPUtil/Float16.h
@@ -0,0 +1,127 @@
+//===-- Definition of float16 data type. ------------------------*- 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_FPUTIL_FLOAT16_H
+#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_FLOAT16_H
+
+#include "src/__support/macros/properties/types.h"
+
+#ifdef LIBC_USE_SOFT_FLOAT16
+
+#include "hdr/stdint_proxy.h"
+#include "src/__support/CPP/type_traits.h"
+#include "src/__support/FPUtil/cast.h"
+#include "src/__support/FPUtil/comparison_operations.h"
+#include "src/__support/FPUtil/dyadic_float.h"
+#include "src/__support/FPUtil/generic/add_sub.h"
+#include "src/__support/FPUtil/generic/div.h"
+#include "src/__support/FPUtil/generic/mul.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace fputil {
+
+struct Float16 {
+ uint16_t bits;
+
+ LIBC_INLINE Float16() = default;
+
+ template <typename T>
+ LIBC_INLINE constexpr explicit Float16(T value)
+ : bits(static_cast<uint16_t>(0U)) {
+ if constexpr (cpp::is_floating_point_v<T>) {
+ bits = fputil::cast<Float16>(value).bits;
+ } else if constexpr (cpp::is_integral_v<T>) {
+ Sign sign = Sign::POS;
+
+ if constexpr (cpp::is_signed_v<T>) {
+ if (value < 0) {
+ sign = Sign::NEG;
+ value = -value;
+ }
+ }
+
+ fputil::DyadicFloat<cpp::numeric_limits<cpp::make_unsigned_t<T>>::digits>
+ xd(sign, 0, value);
+ bits = xd.template as<Float16, /*ShouldSignalExceptions=*/true>().bits;
+
+ } else if constexpr (cpp::is_convertible_v<T, Float16>) {
+ bits = value.operator Float16().bits;
+ }
+ }
+
+ template <cpp::enable_if_t<fputil::get_fp_type<float>() ==
+ fputil::FPType::IEEE754_Binary32,
+ int> = 0>
+ LIBC_INLINE constexpr operator float() const {
+ return fputil::cast<float>(*this);
+ }
+
+ template <typename T, cpp::enable_if_t<cpp::is_integral_v<T>, int> = 0>
+ LIBC_INLINE constexpr explicit operator T() const {
+ return static_cast<T>(static_cast<float>(*this));
+ }
+
+ LIBC_INLINE bool operator==(Float16 other) const {
+ return fputil::equals(*this, other);
+ }
+
+ LIBC_INLINE bool operator!=(Float16 other) const {
+ return !fputil::equals(*this, other);
+ }
+
+ LIBC_INLINE bool operator<(Float16 other) const {
+ return fputil::less_than(*this, other);
+ }
+
+ LIBC_INLINE bool operator<=(Float16 other) const {
+ return fputil::less_than_or_equals(*this, other);
+ }
+
+ LIBC_INLINE bool operator>(Float16 other) const {
+ return fputil::greater_than(*this, other);
+ }
+
+ LIBC_INLINE bool operator>=(Float16 other) const {
+ return fputil::greater_than_or_equals(*this, other);
+ }
+
+ LIBC_INLINE constexpr Float16 operator-() const {
+ fputil::FPBits<float16> result(*this);
+ result.set_sign(result.is_pos() ? Sign::NEG : Sign::POS);
+ return result.get_val();
+ }
+
+ LIBC_INLINE Float16 operator+(Float16 other) const {
+ return fputil::generic::add<Float16>(*this, other);
+ }
+
+ LIBC_INLINE Float16 operator-(Float16 other) const {
+ return fputil::generic::sub<Float16>(*this, other);
+ }
+
+ LIBC_INLINE Float16 operator*(Float16 other) const {
+ return fputil::generic::mul<float16>(*this, other);
+ }
+
+ LIBC_INLINE Float16 operator/(Float16 other) const {
+ return fputil::generic::div<float16>(*this, other);
+ }
+
+ LIBC_INLINE Float16 &operator*=(const Float16 &other) {
+ *this = *this * other;
+ return *this;
+ }
+}; // struct Float16
+
+} // namespace fputil
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LIBC_USE_SOFT_FLOAT16
+
+#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_FLOAT16_H
diff --git a/libc/src/__support/macros/properties/types.h b/libc/src/__support/macros/properties/types.h
index 505fa6d7957f8..68907507b9ffc 100644
--- a/libc/src/__support/macros/properties/types.h
+++ b/libc/src/__support/macros/properties/types.h
@@ -50,8 +50,20 @@
// LIBC_TYPES_HAS_FLOAT16 is provided by
// "include/llvm-libc-macros/float16-macros.h"
#ifdef LIBC_TYPES_HAS_FLOAT16
+
+#ifdef LIBC_USE_SOFT_FLOAT16
+namespace LIBC_NAMESPACE_DECL {
+namespace fputil {
+struct Float16;
+} // namespace fputil
+} // namespace LIBC_NAMESPACE_DECL
+
+using float16 = LIBC_NAMESPACE::fputil::Float16;
+#else
// Type alias for internal use.
using float16 = _Float16;
+#endif
+
#endif // LIBC_TYPES_HAS_FLOAT16
// -- float128 support --------------------------------------------------------
diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt
index 9f4624682eafe..baed5ad3ffea9 100644
--- a/libc/src/__support/math/CMakeLists.txt
+++ b/libc/src/__support/math/CMakeLists.txt
@@ -3236,6 +3236,7 @@ add_header_library(
fabsf16.h
DEPENDS
libc.include.llvm-libc-macros.float16_macros
+ libc.src.__support.FPUtil.Float16
libc.src.__support.FPUtil.basic_operations
libc.src.__support.macros.config
FLAGS
diff --git a/libc/src/__support/math/fabsf16.h b/libc/src/__support/math/fabsf16.h
index 26634361adc73..ca568c680b7c2 100644
--- a/libc/src/__support/math/fabsf16.h
+++ b/libc/src/__support/math/fabsf16.h
@@ -14,6 +14,7 @@
#ifdef LIBC_TYPES_HAS_FLOAT16
#include "src/__support/FPUtil/BasicOperations.h"
+#include "src/__support/FPUtil/Float16.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/properties/architectures.h"
#include "src/__support/macros/properties/compiler.h"
@@ -27,7 +28,8 @@ LIBC_INLINE constexpr float16 fabsf16(float16 x) {
// For x86, GCC generates better code from the generic implementation.
// https://godbolt.org/z/K9orM4hTa
#if defined(__LIBC_MISC_MATH_BASIC_OPS_OPT) && \
- !(defined(LIBC_TARGET_ARCH_IS_X86) && defined(LIBC_COMPILER_IS_GCC))
+ !(defined(LIBC_TARGET_ARCH_IS_X86) && defined(LIBC_COMPILER_IS_GCC)) && \
+ !defined(LIBC_USE_SOFT_FLOAT16)
return __builtin_fabsf16(x);
#else
return fputil::abs(x);
diff --git a/libc/src/math/generic/fabsf16.cpp b/libc/src/math/generic/fabsf16.cpp
index 8e84f78ed564e..9138d54dd5f62 100644
--- a/libc/src/math/generic/fabsf16.cpp
+++ b/libc/src/math/generic/fabsf16.cpp
@@ -6,7 +6,6 @@
//
//===----------------------------------------------------------------------===//
-#include "src/math/fabsf16.h"
#include "src/__support/math/fabsf16.h"
namespace LIBC_NAMESPACE_DECL {
diff --git a/libc/test/src/math/exhaustive/CMakeLists.txt b/libc/test/src/math/exhaustive/CMakeLists.txt
index 2d1301d3a1e66..97355fbbf0278 100644
--- a/libc/test/src/math/exhaustive/CMakeLists.txt
+++ b/libc/test/src/math/exhaustive/CMakeLists.txt
@@ -713,6 +713,23 @@ add_fp_unittest(
-lpthread
)
+add_fp_unittest(
+ float16_test
+ NO_RUN_POSTBUILD
+ NEED_MPFR
+ SUITE
+ libc_math_exhaustive_tests
+ SRCS
+ float16_test.cpp
+ DEPENDS
+ .exhaustive_test
+ libc.src.__support.FPUtil.Float16
+ libc.src.__support.FPUtil.cast
+ libc.src.__support.FPUtil.fp_bits
+ LINK_LIBRARIES
+ -lpthread
+)
+
add_fp_unittest(
bfloat16_test
NO_RUN_POSTBUILD
diff --git a/libc/test/src/math/exhaustive/exhaustive_test.h b/libc/test/src/math/exhaustive/exhaustive_test.h
index 322d774d46a68..04d43214e00a2 100644
--- a/libc/test/src/math/exhaustive/exhaustive_test.h
+++ b/libc/test/src/math/exhaustive/exhaustive_test.h
@@ -8,6 +8,7 @@
#include "src/__support/CPP/type_traits.h"
#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/Float16.h"
#include "src/__support/macros/properties/types.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
diff --git a/libc/test/src/math/exhaustive/float16_test.cpp b/libc/test/src/math/exhaustive/float16_test.cpp
new file mode 100644
index 0000000000000..3d1ba1ab805a6
--- /dev/null
+++ b/libc/test/src/math/exhaustive/float16_test.cpp
@@ -0,0 +1,67 @@
+//===-- Exhaustive tests for float -> float16 conversion ------------------===//
+//
+// 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 "exhaustive_test.h"
+#include "src/__support/FPUtil/cast.h"
+#include "utils/MPFRWrapper/MPCommon.h"
+
+using namespace LIBC_NAMESPACE::fputil;
+namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
+
+template <typename InType>
+struct Float16ConversionChecker : public virtual LIBC_NAMESPACE::testing::Test {
+ using FloatType = InType;
+ using FPBits = LIBC_NAMESPACE::fputil::FPBits<FloatType>;
+ using StorageType = typename FPBits::StorageType;
+
+ // Check in a range, return the number of failures.
+ uint64_t check(StorageType start, StorageType stop,
+ mpfr::RoundingMode rounding) {
+ mpfr::ForceRoundingMode r(rounding);
+ if (!r.success)
+ return (stop > start);
+ StorageType bits = start;
+ uint64_t failed = 0;
+ do {
+ FPBits x_bits(bits);
+ FloatType x = x_bits.get_val();
+
+ const float16 libc_result = cast<float16>(x);
+ const float16 mpfr_result = mpfr::MPFRNumber(x).as<float16>();
+
+ const bool correct =
+ LIBC_NAMESPACE::testing::getMatcher<
+ LIBC_NAMESPACE::testing::TestCond::EQ>(mpfr_result)
+ .match(libc_result);
+
+ failed += (!correct);
+ } while (bits++ < stop);
+ return failed;
+ }
+};
+
+template <typename FloatType>
+using LlvmLibcFloat16ExhaustiveTest =
+ LlvmLibcExhaustiveMathTest<Float16ConversionChecker<FloatType>>;
+using LlvmLibcFloat16FromFloatTest = LlvmLibcFloat16ExhaustiveTest<float>;
+
+// Positive Range: [0, Inf];
+constexpr uint32_t POS_START = 0x0000'0000U;
+constexpr uint32_t POS_STOP = 0x7f80'0000U;
+
+// Negative Range: [-Inf, 0];
+constexpr uint32_t NEG_START = 0xb000'0000U;
+constexpr uint32_t NEG_STOP = 0xff80'0000U;
+
+TEST_F(LlvmLibcFloat16FromFloatTest, PostiveRange) {
+ test_full_range_all_roundings(POS_START, POS_STOP);
+}
+
+TEST_F(LlvmLibcFloat16FromFloatTest, NegativeRange) {
+ test_full_range_all_roundings(NEG_START, NEG_STOP);
+}
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index 81f76fa7681fd..8e670ad2a2851 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -229,6 +229,7 @@ add_fp_unittest(
FAbsTest.h
DEPENDS
libc.src.math.fabsf16
+ libc.src.__support.FPUtil.Float16
)
add_fp_unittest(
diff --git a/libc/test/src/math/smoke/fabsf16_test.cpp b/libc/test/src/math/smoke/fabsf16_test.cpp
index c43bd5090f90b..d4d6dd1d2593a 100644
--- a/libc/test/src/math/smoke/fabsf16_test.cpp
+++ b/libc/test/src/math/smoke/fabsf16_test.cpp
@@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
#include "FAbsTest.h"
-
+#include "src/__support/FPUtil/Float16.h"
#include "src/math/fabsf16.h"
LIST_FABS_TESTS(float16, LIBC_NAMESPACE::fabsf16)
diff --git a/libc/utils/MPFRWrapper/CMakeLists.txt b/libc/utils/MPFRWrapper/CMakeLists.txt
index 73151c61a7fcc..a1546992ce21c 100644
--- a/libc/utils/MPFRWrapper/CMakeLists.txt
+++ b/libc/utils/MPFRWrapper/CMakeLists.txt
@@ -14,6 +14,7 @@ if(LIBC_TESTS_CAN_USE_MPFR OR LIBC_TESTS_CAN_USE_MPC)
libc.src.__support.CPP.string
libc.src.__support.CPP.string_view
libc.src.__support.CPP.type_traits
+ libc.src.__support.FPUtil.Float16
libc.src.__support.FPUtil.bfloat16
libc.src.__support.FPUtil.cast
libc.src.__support.FPUtil.fp_bits
@@ -43,6 +44,7 @@ if(LIBC_TESTS_CAN_USE_MPFR)
libc.hdr.stdint_proxy
libc.src.__support.CPP.array
libc.src.__support.CPP.stringstream
+ libc.src.__support.FPUtil.Float16
libc.src.__support.FPUtil.bfloat16
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.fpbits_str
diff --git a/libc/utils/MPFRWrapper/MPCommon.cpp b/libc/utils/MPFRWrapper/MPCommon.cpp
index 2422bcf45222f..4847f03d1cbfa 100644
--- a/libc/utils/MPFRWrapper/MPCommon.cpp
+++ b/libc/utils/MPFRWrapper/MPCommon.cpp
@@ -10,6 +10,11 @@
#include "src/__support/CPP/string_view.h"
#include "src/__support/FPUtil/bfloat16.h"
+
+#ifdef LIBC_USE_SOFT_FLOAT16
+#include "src/__support/FPUtil/Float16.h"
+#endif
+
#include "src/__support/FPUtil/cast.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/properties/types.h"
@@ -632,7 +637,7 @@ template <> long double MPFRNumber::as<long double>() const {
return mpfr_get_ld(value, mpfr_rounding);
}
-#ifdef LIBC_TYPES_HAS_FLOAT16
+#if defined(LIBC_TYPES_HAS_FLOAT16) || defined(LIBC_USE_SOFT_FLOAT16)
template <> float16 MPFRNumber::as<float16>() const {
// TODO: Either prove that this cast won't cause double-rounding errors, or
// find a better way to get a float16.
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp
index d585baa2e0d2c..b928edeebba4b 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp
@@ -11,6 +11,7 @@
#include "src/__support/CPP/array.h"
#include "src/__support/CPP/stringstream.h"
+#include "src/__support/FPUtil/Float16.h"
#include "src/__support/FPUtil/bfloat16.h"
#include "src/__support/FPUtil/fpbits_str.h"
#include "src/__support/macros/config.h"
More information about the libc-commits
mailing list