[libc-commits] [libc] dd82a72 - [libc] Float80 Emulation in LLVM libc (#214447)
via libc-commits
libc-commits at lists.llvm.org
Mon Aug 24 20:28:35 PDT 2026
Author: Zorojuro
Date: 2026-08-25T08:58:27+05:30
New Revision: dd82a72885c82891fce73dbca5af8b6d9db2af34
URL: https://github.com/llvm/llvm-project/commit/dd82a72885c82891fce73dbca5af8b6d9db2af34
DIFF: https://github.com/llvm/llvm-project/commit/dd82a72885c82891fce73dbca5af8b6d9db2af34.diff
LOG: [libc] Float80 Emulation in LLVM libc (#214447)
Continued from https://github.com/llvm/llvm-project/pull/211350
---------
Co-authored-by: OverMighty <its.overmighty at gmail.com>
Added:
libc/src/__support/FPUtil/float80.h
libc/test/src/__support/FPUtil/float80_test.cpp
Modified:
libc/src/__support/CPP/type_traits/is_floating_point.h
libc/src/__support/FPUtil/CMakeLists.txt
libc/src/__support/FPUtil/FPBits.h
libc/src/__support/FPUtil/cast.h
libc/src/__support/FPUtil/dyadic_float.h
libc/src/__support/macros/properties/types.h
libc/test/src/__support/FPUtil/CMakeLists.txt
Removed:
################################################################################
diff --git a/libc/src/__support/CPP/type_traits/is_floating_point.h b/libc/src/__support/CPP/type_traits/is_floating_point.h
index b8c3bf05b4ebb..4a5984e38a8cf 100644
--- a/libc/src/__support/CPP/type_traits/is_floating_point.h
+++ b/libc/src/__support/CPP/type_traits/is_floating_point.h
@@ -44,7 +44,10 @@ template <typename T> struct is_floating_point {
bfloat16
,
- fputil::Float128>();
+ fputil::Float128
+
+ ,
+ fputil::Float80>();
};
template <typename T>
LIBC_INLINE_VAR constexpr bool is_floating_point_v =
diff --git a/libc/src/__support/FPUtil/CMakeLists.txt b/libc/src/__support/FPUtil/CMakeLists.txt
index 4673ef794c80b..96fbb6d80a669 100644
--- a/libc/src/__support/FPUtil/CMakeLists.txt
+++ b/libc/src/__support/FPUtil/CMakeLists.txt
@@ -313,4 +313,22 @@ add_header_library(
libc.src.__support.uint128
)
+add_header_library(
+ float80
+ HDRS
+ float80.h
+ DEPENDS
+ .cast
+ .comparison_operations
+ .dyadic_float
+ libc.hdr.stdint_proxy
+ 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.attributes
+ libc.src.__support.macros.config
+ libc.src.__support.uint128
+)
+
add_subdirectory(generic)
diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 0a7aee7ae232e..6127dede03f4a 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -816,6 +816,8 @@ template <typename T> LIBC_INLINE static constexpr FPType get_fp_type() {
return FPType::BFloat16;
else if constexpr (cpp::is_same_v<UnqualT, Float128>)
return FPType::IEEE754_Binary128;
+ else if constexpr (cpp::is_same_v<UnqualT, Float80>)
+ return FPType::X86_Binary80;
else
static_assert(cpp::always_false<UnqualT>, "Unsupported type");
}
diff --git a/libc/src/__support/FPUtil/cast.h b/libc/src/__support/FPUtil/cast.h
index 4fc5ea0893ebe..2a9bfd5a9f62e 100644
--- a/libc/src/__support/FPUtil/cast.h
+++ b/libc/src/__support/FPUtil/cast.h
@@ -33,7 +33,9 @@ cast(InType x) {
if constexpr (cpp::is_same_v<OutType, bfloat16> ||
cpp::is_same_v<InType, bfloat16> ||
cpp::is_same_v<OutType, Float128> ||
- cpp::is_same_v<InType, Float128>
+ cpp::is_same_v<InType, Float128> ||
+ cpp::is_same_v<OutType, Float80> ||
+ cpp::is_same_v<InType, Float80>
#if defined(LIBC_TYPES_HAS_FLOAT16) && !defined(__LIBC_USE_FLOAT16_CONVERSION)
|| cpp::is_same_v<OutType, float16> ||
cpp::is_same_v<InType, float16>
diff --git a/libc/src/__support/FPUtil/dyadic_float.h b/libc/src/__support/FPUtil/dyadic_float.h
index 2effcfcb4a34d..e32906e3cf9cf 100644
--- a/libc/src/__support/FPUtil/dyadic_float.h
+++ b/libc/src/__support/FPUtil/dyadic_float.h
@@ -256,10 +256,12 @@ template <size_t Bits> struct DyadicFloat {
static_cast<StorageType>(unbiased_exp + FPBits::EXP_BIAS);
}
- MantissaType round_mask = MantissaType(1) << (extra_fraction_len - 1);
- round = (mantissa & round_mask) != 0;
- MantissaType sticky_mask = round_mask - 1;
- sticky = (mantissa & sticky_mask) != 0;
+ if (extra_fraction_len > 0) {
+ MantissaType round_mask = MantissaType(1) << (extra_fraction_len - 1);
+ round = (mantissa & round_mask) != 0;
+ MantissaType sticky_mask = round_mask - 1;
+ sticky = (mantissa & sticky_mask) != 0;
+ }
out_mantissa = static_cast<StorageType>(mantissa >> extra_fraction_len);
}
@@ -438,7 +440,8 @@ template <size_t Bits> struct DyadicFloat {
(FPBits<T>::FRACTION_LEN < Bits),
void>>
LIBC_INLINE LIBC_CONSTEXPR_DEFAULT T as() const {
- if constexpr (cpp::is_same_v<T, bfloat16> || cpp::is_same_v<T, Float128>
+ if constexpr (cpp::is_same_v<T, bfloat16> || cpp::is_same_v<T, Float128> ||
+ cpp::is_same_v<T, Float80>
#if defined(LIBC_TYPES_HAS_FLOAT16) && !defined(__LIBC_USE_FLOAT16_CONVERSION)
|| cpp::is_same_v<T, float16>
#endif
diff --git a/libc/src/__support/FPUtil/float80.h b/libc/src/__support/FPUtil/float80.h
new file mode 100644
index 0000000000000..4529fe396a6dc
--- /dev/null
+++ b/libc/src/__support/FPUtil/float80.h
@@ -0,0 +1,103 @@
+//===-- Definition for Float80 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_FLOAT80_H
+#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_FLOAT80_H
+
+#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/attributes.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/uint128.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace fputil {
+
+struct Float80 {
+ UInt128 bits;
+
+ LIBC_INLINE Float80() = default;
+ LIBC_INLINE constexpr Float80(const Float80 &) = default;
+ LIBC_INLINE constexpr Float80(Float80 &&) = default;
+ LIBC_INLINE constexpr Float80 &operator=(const Float80 &) = default;
+ LIBC_INLINE constexpr Float80 &operator=(Float80 &&) = default;
+
+ // Floating point type and integer type
+ template <typename T>
+ LIBC_INLINE constexpr explicit Float80(T value) : bits(0U) {
+ if constexpr (cpp::is_floating_point_v<T>) {
+ bits = fputil::cast<Float80>(value).bits;
+ } else if constexpr (cpp::is_integral_v<T>) {
+ Sign sign = Sign::POS;
+ auto unsigned_value = static_cast<cpp::make_unsigned_t<T>>(value);
+
+ if constexpr (cpp::is_signed_v<T>) {
+ if (value < 0) {
+ sign = Sign::NEG;
+ unsigned_value = -unsigned_value;
+ }
+ }
+
+ fputil::DyadicFloat<FPBits<Float80>::STORAGE_LEN> xd(sign, 0,
+ unsigned_value);
+ bits = xd.template as<Float80, /*ShouldSignalExceptions=*/true>().bits;
+
+ } else if constexpr (cpp::is_convertible_v<T, Float80>) {
+ bits = value.operator Float80().bits;
+ } else {
+ bits = fputil::cast<Float80>(static_cast<float>(value)).bits;
+ }
+ }
+
+ template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T> &&
+ !cpp::is_same_v<T, Float80>,
+ int> = 0>
+ LIBC_INLINE LIBC_CONSTEXPR_DEFAULT operator T() const {
+ return fputil::cast<T>(*this);
+ }
+
+ template <typename T, cpp::enable_if_t<cpp::is_integral_v<T>, int> = 0>
+ LIBC_INLINE constexpr explicit operator T() const {
+ constexpr T MIN_T = cpp::numeric_limits<T>::min();
+ constexpr T MAX_T = cpp::numeric_limits<T>::max();
+ FPBits<Float80> x_bits(*this);
+ // Raise FE_INVALID for inf and NaN
+ if (x_bits.is_inf_or_nan()) {
+ raise_except_if_required(FE_INVALID);
+ return x_bits.is_neg() ? MIN_T : MAX_T;
+ }
+ int exponent = x_bits.get_explicit_exponent();
+ constexpr int EXPONENT_LIMIT = cpp::numeric_limits<T>::digits;
+ if (exponent > EXPONENT_LIMIT) {
+ raise_except_if_required(FE_INVALID);
+ return x_bits.is_neg() ? MIN_T : MAX_T;
+ } else if (exponent == EXPONENT_LIMIT) {
+ if (x_bits.is_pos() || x_bits.get_mantissa() != 0) {
+ raise_except_if_required(FE_INVALID);
+ return x_bits.is_neg() ? MIN_T : MAX_T;
+ }
+ }
+
+ int x_bits_exp = exponent - FPBits<Float80>::FRACTION_LEN;
+ // sign * 2^(exp-bias) * mantissa
+ DyadicFloat<FPBits<Float80>::STORAGE_LEN> xd(
+ x_bits.sign(), x_bits_exp, x_bits.get_explicit_mantissa());
+ return static_cast<T>(xd.as_mantissa_type());
+ }
+};
+
+} // namespace fputil
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_Float80_H
diff --git a/libc/src/__support/macros/properties/types.h b/libc/src/__support/macros/properties/types.h
index ec42b2ef033a6..aad4c41005c45 100644
--- a/libc/src/__support/macros/properties/types.h
+++ b/libc/src/__support/macros/properties/types.h
@@ -93,6 +93,16 @@ struct Float128;
// TODO: Commented till we modify all required functions to support emulated
// Float128.
+// -- Emulated float80 support ------------------------------------------------
+
+namespace LIBC_NAMESPACE_DECL {
+namespace fputil {
+struct Float80;
+}
+} // namespace LIBC_NAMESPACE_DECL
+
+using float80 = LIBC_NAMESPACE::fputil::Float80;
+
// -- bfloat16 support ---------------------------------------------------------
namespace LIBC_NAMESPACE_DECL {
diff --git a/libc/test/src/__support/FPUtil/CMakeLists.txt b/libc/test/src/__support/FPUtil/CMakeLists.txt
index 87fd10aaacd90..b5d9ca2331697 100644
--- a/libc/test/src/__support/FPUtil/CMakeLists.txt
+++ b/libc/test/src/__support/FPUtil/CMakeLists.txt
@@ -52,6 +52,18 @@ add_fp_unittest(
libc.src.__support.macros.properties.types
)
+add_fp_unittest(
+ float80_test
+ SUITE
+ libc-fputil-tests
+ SRCS
+ float80_test.cpp
+ DEPENDS
+ libc.hdr.limits_macros
+ libc.src.__support.FPUtil.fenv_impl
+ libc.src.__support.FPUtil.float80
+)
+
# TODO: Temporally disable bfloat16 test until MPCommon target is updated
# https://github.com/llvm/llvm-project/pull/149678
if(LLVM_LIBC_FULL_BUILD)
diff --git a/libc/test/src/__support/FPUtil/float80_test.cpp b/libc/test/src/__support/FPUtil/float80_test.cpp
new file mode 100644
index 0000000000000..72c1155f8c6d3
--- /dev/null
+++ b/libc/test/src/__support/FPUtil/float80_test.cpp
@@ -0,0 +1,112 @@
+//===-- Unittests for Float80 emulated type -------------------------------===//
+//
+// 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/limits_macros.h"
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/float80.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using LIBC_NAMESPACE::Sign;
+using LIBC_NAMESPACE::fputil::Float80;
+using FPBits = LIBC_NAMESPACE::fputil::FPBits<Float80>;
+
+TEST(LlvmLibcFloat80Test, IntegerConversion) {
+ // Float80 to Integer conversion test
+ ASSERT_EQ(static_cast<int>(Float80(0.0f)), 0);
+ ASSERT_EQ(static_cast<int>(Float80(1.0f)), 1);
+ ASSERT_EQ(static_cast<long long>(Float80(1000000000.0)),
+ static_cast<long long>(1000000000));
+ ASSERT_EQ(static_cast<unsigned>(Float80(7.0f)), 7U);
+ ASSERT_EQ(static_cast<int>(Float80(1.9f)), 1);
+
+ // Border values
+ LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
+ ASSERT_EQ(static_cast<int>(Float80(INT_MAX)), INT_MAX);
+ ASSERT_EQ(static_cast<long long>(Float80(LLONG_MAX)), LLONG_MAX);
+ ASSERT_EQ(static_cast<unsigned>(Float80(UINT_MAX)), UINT_MAX);
+ EXPECT_EQ(LIBC_NAMESPACE::fputil::test_except(FE_INVALID), 0);
+
+ // FP exceptions
+ LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
+ ASSERT_EQ(static_cast<int>(FPBits::quiet_nan().get_val()), INT_MAX);
+ EXPECT_FP_EXCEPTION(FE_INVALID);
+
+ LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
+ ASSERT_EQ(static_cast<int>(FPBits::inf().get_val()), INT_MAX);
+ EXPECT_FP_EXCEPTION(FE_INVALID);
+
+ // Extreme values
+ LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
+ ASSERT_EQ(static_cast<int>(Float80(1e300)), INT_MAX);
+ EXPECT_FP_EXCEPTION(FE_INVALID);
+
+ LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
+ ASSERT_EQ(static_cast<int>(FPBits::inf(Sign::POS).get_val()), INT_MAX);
+ EXPECT_FP_EXCEPTION(FE_INVALID);
+
+ // Small values
+ LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
+ ASSERT_EQ(static_cast<int>(Float80(1e-300)), 0);
+ ASSERT_EQ(static_cast<int>(Float80(0.5)), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::fputil::test_except(FE_INVALID), 0);
+}
+
+#ifdef LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
+TEST(LlvmLibcFloat80Test, randomTest) {
+ using FPBitsL = LIBC_NAMESPACE::fputil::FPBits<long double>;
+
+ const FPBitsL::StorageType EDGE_CASES[] = {
+ FPBitsL::zero(Sign::POS).uintval(),
+ FPBitsL::zero(Sign::NEG).uintval(),
+ FPBitsL::inf(Sign::POS).uintval(),
+ FPBitsL::inf(Sign::NEG).uintval(),
+ FPBitsL::quiet_nan().uintval(),
+ FPBitsL::min_subnormal(Sign::POS).uintval(),
+ FPBitsL::min_subnormal(Sign::NEG).uintval(),
+ FPBitsL::max_subnormal(Sign::POS).uintval(),
+ FPBitsL::max_subnormal(Sign::NEG).uintval(),
+ FPBitsL::min_normal(Sign::POS).uintval(),
+ FPBitsL::min_normal(Sign::NEG).uintval(),
+ FPBitsL::max_normal(Sign::POS).uintval(),
+ FPBitsL::max_normal(Sign::NEG).uintval(),
+ FPBitsL::one(Sign::POS).uintval(),
+ FPBitsL::one(Sign::NEG).uintval(),
+ };
+
+ for (FPBitsL::StorageType bits : EDGE_CASES) {
+ long double native = FPBitsL(bits).get_val();
+
+ Float80 f80_temp = LIBC_NAMESPACE::fputil::cast<Float80>(native);
+ EXPECT_EQ(FPBits(f80_temp).uintval(), bits);
+
+ long double ld_temp =
+ LIBC_NAMESPACE::fputil::cast<long double>(FPBits(bits).get_val());
+ EXPECT_EQ(FPBitsL(ld_temp).uintval(), bits);
+ }
+}
+
+#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
+
+TEST(LlvmLibcFloat80Test, FromIntegralTypes) {
+ // Integer to Float80 conversion test
+ ASSERT_EQ(FPBits(Float80(42)).uintval(), FPBits(Float80(42.0f)).uintval());
+ ASSERT_EQ(FPBits(Float80(0)).uintval(), FPBits(Float80(0.0f)).uintval());
+ ASSERT_EQ(FPBits(Float80(7U)).uintval(), FPBits(Float80(7.0f)).uintval());
+ ASSERT_EQ(FPBits(Float80(123456789LL)).uintval(),
+ FPBits(Float80(123456789.0)).uintval());
+
+ // 2147483648.0 or 2^31 is out of bound in signed and not in unsigned
+ LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
+ ASSERT_EQ(static_cast<int>(Float80(2147483648.0)), INT_MAX);
+ EXPECT_FP_EXCEPTION(FE_INVALID);
+
+ LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
+ ASSERT_EQ(static_cast<unsigned>(Float80(2147483648.0)), 2147483648U);
+ EXPECT_EQ(LIBC_NAMESPACE::fputil::test_except(FE_INVALID), 0);
+}
More information about the libc-commits
mailing list