[libc-commits] [libc] [libc] Float80 Emulation in LLVM libc (PR #214447)
via libc-commits
libc-commits at lists.llvm.org
Sat Aug 8 00:04:19 PDT 2026
https://github.com/Sukumarsawant updated https://github.com/llvm/llvm-project/pull/214447
>From e1b3c15d97cd7a096a64ce17160209564b949874 Mon Sep 17 00:00:00 2001
From: Sukumarsawant <sawantsukumar at gmail.com>
Date: Thu, 23 Jul 2026 00:10:21 +0530
Subject: [PATCH 01/13] initial temp skeleton
---
.../CPP/type_traits/is_floating_point.h | 4 +-
libc/src/__support/FPUtil/CMakeLists.txt | 18 ++
libc/src/__support/FPUtil/FPBits.h | 2 +
libc/src/__support/FPUtil/float80.h | 161 ++++++++++++++++++
libc/src/__support/macros/properties/types.h | 9 +
libc/test/src/__support/FPUtil/CMakeLists.txt | 12 ++
.../src/__support/FPUtil/float80_test.cpp | 19 +++
7 files changed, 224 insertions(+), 1 deletion(-)
create mode 100644 libc/src/__support/FPUtil/float80.h
create mode 100644 libc/test/src/__support/FPUtil/float80_test.cpp
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 283c4fcefb6fe..26b755cd0126e 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,9 @@ 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 8b6ac8027b52d..10f399bce9c6b 100644
--- a/libc/src/__support/FPUtil/CMakeLists.txt
+++ b/libc/src/__support/FPUtil/CMakeLists.txt
@@ -312,4 +312,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 83219b7573f46..143e45cd075a1 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -815,6 +815,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::IEEE754_Binary128;
else
static_assert(cpp::always_false<UnqualT>, "Unsupported type");
}
diff --git a/libc/src/__support/FPUtil/float80.h b/libc/src/__support/FPUtil/float80.h
new file mode 100644
index 0000000000000..edd63aab47ba7
--- /dev/null
+++ b/libc/src/__support/FPUtil/float80.h
@@ -0,0 +1,161 @@
+//===-- 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 {
+ 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);
+ }
+ int x_bits_exp =
+ x_bits.get_explicit_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());
+ }
+
+ // unary
+ LIBC_INLINE LIBC_BIT_CAST_CONSTEXPR Float80 operator-() const {
+ fputil::FPBits<Float80> result(*this);
+ result.set_sign(result.is_pos() ? Sign::NEG : Sign::POS);
+ return result.get_val();
+ }
+ // operator overloads
+ LIBC_INLINE constexpr Float80 operator+(const Float80 &other) const {
+ return fputil::generic::add<Float80>(*this, other);
+ }
+
+ LIBC_INLINE constexpr Float80 operator-(const Float80 &other) const {
+ return fputil::generic::sub<Float80>(*this, other);
+ }
+
+ LIBC_INLINE constexpr Float80 operator*(const Float80 &other) const {
+ return fputil::generic::mul<Float80>(*this, other);
+ }
+
+ LIBC_INLINE constexpr Float80 operator/(const Float80 &other) const {
+ return fputil::generic::div<Float80>(*this, other);
+ }
+
+ LIBC_INLINE constexpr Float80 &operator*=(const Float80 &other) {
+ *this = *this * other;
+ return *this;
+ }
+
+ LIBC_INLINE constexpr Float80 &operator+=(const Float80 &other) {
+ *this = *this + other;
+ return *this;
+ }
+
+ LIBC_INLINE constexpr Float80 &operator-=(const Float80 &other) {
+ *this = *this - other;
+ return *this;
+ }
+
+ LIBC_INLINE constexpr Float80 &operator/=(const Float80 &other) {
+ *this = *this / other;
+ return *this;
+ }
+
+ LIBC_INLINE constexpr bool operator==(const Float80 &other) const {
+ return fputil::equals(*this, other);
+ }
+
+ LIBC_INLINE constexpr bool operator!=(const Float80 &other) const {
+ return !fputil::equals(*this, other);
+ }
+
+ LIBC_INLINE constexpr bool operator<(const Float80 &other) const {
+ return fputil::less_than(*this, other);
+ }
+
+ LIBC_INLINE constexpr bool operator<=(const Float80 &other) const {
+ return fputil::less_than_or_equals(*this, other);
+ }
+
+ LIBC_INLINE constexpr bool operator>(const Float80 &other) const {
+ return fputil::greater_than(*this, other);
+ }
+
+ LIBC_INLINE constexpr bool operator>=(const Float80 &other) const {
+ return fputil::greater_than_or_equals(*this, other);
+ }
+};
+
+static_assert(LIBC_NAMESPACE::cpp::is_trivially_constructible<
+ LIBC_NAMESPACE::fputil::Float80>::value);
+static_assert(LIBC_NAMESPACE::cpp::is_trivially_copyable<
+ LIBC_NAMESPACE::fputil::Float80>::value);
+
+} // 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 505fa6d7957f8..a76d1f192e1bb 100644
--- a/libc/src/__support/macros/properties/types.h
+++ b/libc/src/__support/macros/properties/types.h
@@ -73,6 +73,15 @@ 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 float128 = LIBC_NAMESPACE::fputil::Float128;
+
// -- 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 cab95df0e709c..a236dfeb5bcbd 100644
--- a/libc/test/src/__support/FPUtil/CMakeLists.txt
+++ b/libc/test/src/__support/FPUtil/CMakeLists.txt
@@ -51,6 +51,18 @@ add_fp_unittest(
libc.src.__support.FPUtil.float128
)
+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..c7245fa45cd1a
--- /dev/null
+++ b/libc/test/src/__support/FPUtil/float80_test.cpp
@@ -0,0 +1,19 @@
+//===-- 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, temp) { Float80 a(1.0f); }
>From 65ac5d7f90d264792c78cf5128366d4372519e01 Mon Sep 17 00:00:00 2001
From: Sukumarsawant <sawantsukumar at gmail.com>
Date: Thu, 23 Jul 2026 00:15:36 +0530
Subject: [PATCH 02/13] nit
---
libc/src/__support/macros/properties/types.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libc/src/__support/macros/properties/types.h b/libc/src/__support/macros/properties/types.h
index a76d1f192e1bb..4d6359c186634 100644
--- a/libc/src/__support/macros/properties/types.h
+++ b/libc/src/__support/macros/properties/types.h
@@ -80,7 +80,7 @@ namespace fputil {
struct Float80;
}
} // namespace LIBC_NAMESPACE_DECL
-using float128 = LIBC_NAMESPACE::fputil::Float128;
+using float80 = LIBC_NAMESPACE::fputil::Float80;
// -- bfloat16 support ---------------------------------------------------------
>From 35e5f6d50f0290253db255a34f0e97d754daf85b Mon Sep 17 00:00:00 2001
From: Sukumarsawant <sawantsukumar at gmail.com>
Date: Thu, 30 Jul 2026 20:30:14 +0530
Subject: [PATCH 03/13] Removed temporarliy for 128bit container only
---
libc/src/__support/FPUtil/FPBits.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 143e45cd075a1..41a87796c7575 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -127,11 +127,11 @@ template <> struct FPLayout<FPType::IEEE754_Binary128> {
};
template <> struct FPLayout<FPType::X86_Binary80> {
-#if __SIZEOF_LONG_DOUBLE__ == 12
- using StorageType = UInt<__SIZEOF_LONG_DOUBLE__ * CHAR_BIT>;
-#else
+// #if __SIZEOF_LONG_DOUBLE__ == 12
+// using StorageType = UInt<__SIZEOF_LONG_DOUBLE__ * CHAR_BIT>;
+// #else
using StorageType = UInt128;
-#endif
+// #endif
LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1;
LIBC_INLINE_VAR static constexpr int EXP_LEN = 15;
LIBC_INLINE_VAR static constexpr int SIG_LEN = 64;
>From 0e76ecde4d24cc84e662f13d14319008037fff96 Mon Sep 17 00:00:00 2001
From: Sukumarsawant <sawantsukumar at gmail.com>
Date: Thu, 30 Jul 2026 20:36:32 +0530
Subject: [PATCH 04/13] test
---
libc/src/__support/FPUtil/FPBits.h | 8 ++++----
libc/src/__support/FPUtil/dyadic_float.h | 3 ++-
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 41a87796c7575..783e2fe0d5e6d 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -127,11 +127,11 @@ template <> struct FPLayout<FPType::IEEE754_Binary128> {
};
template <> struct FPLayout<FPType::X86_Binary80> {
-// #if __SIZEOF_LONG_DOUBLE__ == 12
-// using StorageType = UInt<__SIZEOF_LONG_DOUBLE__ * CHAR_BIT>;
-// #else
+ // #if __SIZEOF_LONG_DOUBLE__ == 12
+ // using StorageType = UInt<__SIZEOF_LONG_DOUBLE__ * CHAR_BIT>;
+ // #else
using StorageType = UInt128;
-// #endif
+ // #endif
LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1;
LIBC_INLINE_VAR static constexpr int EXP_LEN = 15;
LIBC_INLINE_VAR static constexpr int SIG_LEN = 64;
diff --git a/libc/src/__support/FPUtil/dyadic_float.h b/libc/src/__support/FPUtil/dyadic_float.h
index c218632131934..2acf8079a0520 100644
--- a/libc/src/__support/FPUtil/dyadic_float.h
+++ b/libc/src/__support/FPUtil/dyadic_float.h
@@ -438,7 +438,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
>From f9a31829223327aeceac8e51001464ecbe8ba1a2 Mon Sep 17 00:00:00 2001
From: Sukumarsawant <sawantsukumar at gmail.com>
Date: Thu, 6 Aug 2026 13:10:13 +0530
Subject: [PATCH 05/13] nit
---
libc/src/__support/FPUtil/FPBits.h | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 783e2fe0d5e6d..15eea13e286d3 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -127,11 +127,11 @@ template <> struct FPLayout<FPType::IEEE754_Binary128> {
};
template <> struct FPLayout<FPType::X86_Binary80> {
- // #if __SIZEOF_LONG_DOUBLE__ == 12
- // using StorageType = UInt<__SIZEOF_LONG_DOUBLE__ * CHAR_BIT>;
- // #else
+ #if __SIZEOF_LONG_DOUBLE__ == 12
+ using StorageType = UInt<__SIZEOF_LONG_DOUBLE__ * CHAR_BIT>;
+ #else
using StorageType = UInt128;
- // #endif
+ #endif
LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1;
LIBC_INLINE_VAR static constexpr int EXP_LEN = 15;
LIBC_INLINE_VAR static constexpr int SIG_LEN = 64;
@@ -816,7 +816,7 @@ template <typename T> LIBC_INLINE static constexpr FPType get_fp_type() {
else if constexpr (cpp::is_same_v<UnqualT, Float128>)
return FPType::IEEE754_Binary128;
else if constexpr (cpp::is_same_v<UnqualT, Float80>)
- return FPType::IEEE754_Binary128;
+ return FPType::IEEE754_Binary80;
else
static_assert(cpp::always_false<UnqualT>, "Unsupported type");
}
>From 37e2bf4934b3e4afaeeb3f416edba3216e1a8b0a Mon Sep 17 00:00:00 2001
From: Sukumarsawant <sawantsukumar at gmail.com>
Date: Thu, 6 Aug 2026 13:15:10 +0530
Subject: [PATCH 06/13] formatting
---
libc/src/__support/FPUtil/FPBits.h | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 15eea13e286d3..d2d56ef19fcb2 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -127,11 +127,11 @@ template <> struct FPLayout<FPType::IEEE754_Binary128> {
};
template <> struct FPLayout<FPType::X86_Binary80> {
- #if __SIZEOF_LONG_DOUBLE__ == 12
- using StorageType = UInt<__SIZEOF_LONG_DOUBLE__ * CHAR_BIT>;
- #else
+#if __SIZEOF_LONG_DOUBLE__ == 12
+ using StorageType = UInt<__SIZEOF_LONG_DOUBLE__ * CHAR_BIT>;
+#else
using StorageType = UInt128;
- #endif
+#endif
LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1;
LIBC_INLINE_VAR static constexpr int EXP_LEN = 15;
LIBC_INLINE_VAR static constexpr int SIG_LEN = 64;
@@ -816,7 +816,7 @@ template <typename T> LIBC_INLINE static constexpr FPType get_fp_type() {
else if constexpr (cpp::is_same_v<UnqualT, Float128>)
return FPType::IEEE754_Binary128;
else if constexpr (cpp::is_same_v<UnqualT, Float80>)
- return FPType::IEEE754_Binary80;
+ return FPType::X86_Binary80;
else
static_assert(cpp::always_false<UnqualT>, "Unsupported type");
}
>From 19b795a107301d12227b0f7af6bf7ecda96e6738 Mon Sep 17 00:00:00 2001
From: Sukumarsawant <sawantsukumar at gmail.com>
Date: Thu, 6 Aug 2026 13:24:45 +0530
Subject: [PATCH 07/13] add fputiL::cast support
---
libc/src/__support/FPUtil/cast.h | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
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>
>From d2b5f5a355add1a247d1ec11e251e28decd65f7c Mon Sep 17 00:00:00 2001
From: Sukumarsawant <sawantsukumar at gmail.com>
Date: Thu, 6 Aug 2026 15:50:02 +0530
Subject: [PATCH 08/13] feat: add tests
---
libc/src/__support/FPUtil/float80.h | 85 ++++---------------
.../src/__support/FPUtil/float80_test.cpp | 59 +++++++++++++
2 files changed, 75 insertions(+), 69 deletions(-)
diff --git a/libc/src/__support/FPUtil/float80.h b/libc/src/__support/FPUtil/float80.h
index edd63aab47ba7..d6823f7bedb76 100644
--- a/libc/src/__support/FPUtil/float80.h
+++ b/libc/src/__support/FPUtil/float80.h
@@ -69,85 +69,32 @@ struct Float80 {
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 x_bits_exp =
- x_bits.get_explicit_exponent() - FPBits<Float80>::FRACTION_LEN;
+ 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());
}
-
- // unary
- LIBC_INLINE LIBC_BIT_CAST_CONSTEXPR Float80 operator-() const {
- fputil::FPBits<Float80> result(*this);
- result.set_sign(result.is_pos() ? Sign::NEG : Sign::POS);
- return result.get_val();
- }
- // operator overloads
- LIBC_INLINE constexpr Float80 operator+(const Float80 &other) const {
- return fputil::generic::add<Float80>(*this, other);
- }
-
- LIBC_INLINE constexpr Float80 operator-(const Float80 &other) const {
- return fputil::generic::sub<Float80>(*this, other);
- }
-
- LIBC_INLINE constexpr Float80 operator*(const Float80 &other) const {
- return fputil::generic::mul<Float80>(*this, other);
- }
-
- LIBC_INLINE constexpr Float80 operator/(const Float80 &other) const {
- return fputil::generic::div<Float80>(*this, other);
- }
-
- LIBC_INLINE constexpr Float80 &operator*=(const Float80 &other) {
- *this = *this * other;
- return *this;
- }
-
- LIBC_INLINE constexpr Float80 &operator+=(const Float80 &other) {
- *this = *this + other;
- return *this;
- }
-
- LIBC_INLINE constexpr Float80 &operator-=(const Float80 &other) {
- *this = *this - other;
- return *this;
- }
-
- LIBC_INLINE constexpr Float80 &operator/=(const Float80 &other) {
- *this = *this / other;
- return *this;
- }
-
- LIBC_INLINE constexpr bool operator==(const Float80 &other) const {
- return fputil::equals(*this, other);
- }
-
- LIBC_INLINE constexpr bool operator!=(const Float80 &other) const {
- return !fputil::equals(*this, other);
- }
-
- LIBC_INLINE constexpr bool operator<(const Float80 &other) const {
- return fputil::less_than(*this, other);
- }
-
- LIBC_INLINE constexpr bool operator<=(const Float80 &other) const {
- return fputil::less_than_or_equals(*this, other);
- }
-
- LIBC_INLINE constexpr bool operator>(const Float80 &other) const {
- return fputil::greater_than(*this, other);
- }
-
- LIBC_INLINE constexpr bool operator>=(const Float80 &other) const {
- return fputil::greater_than_or_equals(*this, other);
- }
};
static_assert(LIBC_NAMESPACE::cpp::is_trivially_constructible<
diff --git a/libc/test/src/__support/FPUtil/float80_test.cpp b/libc/test/src/__support/FPUtil/float80_test.cpp
index c7245fa45cd1a..68b38a63c23b4 100644
--- a/libc/test/src/__support/FPUtil/float80_test.cpp
+++ b/libc/test/src/__support/FPUtil/float80_test.cpp
@@ -17,3 +17,62 @@ using LIBC_NAMESPACE::fputil::Float80;
using FPBits = LIBC_NAMESPACE::fputil::FPBits<Float80>;
TEST(LlvmLibcFloat80Test, temp) { Float80 a(1.0f); }
+
+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);
+}
+
+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);
+}
>From e71ee98fa7853c6cdbd0d58a96b80166ad6c6135 Mon Sep 17 00:00:00 2001
From: Sukumarsawant <sawantsukumar at gmail.com>
Date: Thu, 6 Aug 2026 16:56:36 +0530
Subject: [PATCH 09/13] add explicit bit handling for dyadic_float as per
FPBits
---
libc/src/__support/FPUtil/dyadic_float.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/libc/src/__support/FPUtil/dyadic_float.h b/libc/src/__support/FPUtil/dyadic_float.h
index aa731871cb06b..20fee0582bbfe 100644
--- a/libc/src/__support/FPUtil/dyadic_float.h
+++ b/libc/src/__support/FPUtil/dyadic_float.h
@@ -262,6 +262,8 @@ template <size_t Bits> struct DyadicFloat {
sticky = (mantissa & sticky_mask) != 0;
out_mantissa = static_cast<StorageType>(mantissa >> extra_fraction_len);
+ if constexpr (get_fp_type<T>() == FPType::X86_Binary80)
+ out_mantissa |= FPBits::EXPLICIT_BIT_MASK;
}
bool lsb = (out_mantissa & 1) != 0;
>From d9bcef82594e6286da4db01cbc93ec18d66fb545 Mon Sep 17 00:00:00 2001
From: Sukumarsawant <sawantsukumar at gmail.com>
Date: Thu, 6 Aug 2026 16:58:38 +0530
Subject: [PATCH 10/13] nit
---
libc/src/__support/FPUtil/dyadic_float.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/libc/src/__support/FPUtil/dyadic_float.h b/libc/src/__support/FPUtil/dyadic_float.h
index 20fee0582bbfe..6522c85f73988 100644
--- a/libc/src/__support/FPUtil/dyadic_float.h
+++ b/libc/src/__support/FPUtil/dyadic_float.h
@@ -262,6 +262,7 @@ template <size_t Bits> struct DyadicFloat {
sticky = (mantissa & sticky_mask) != 0;
out_mantissa = static_cast<StorageType>(mantissa >> extra_fraction_len);
+ // Takes into consideration the explicit bit for number for types like float 80
if constexpr (get_fp_type<T>() == FPType::X86_Binary80)
out_mantissa |= FPBits::EXPLICIT_BIT_MASK;
}
>From a3dab770248d68baf7dbf3646d873eacde107152 Mon Sep 17 00:00:00 2001
From: Sukumarsawant <sawantsukumar at gmail.com>
Date: Thu, 6 Aug 2026 17:03:13 +0530
Subject: [PATCH 11/13] nit
---
libc/src/__support/FPUtil/dyadic_float.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libc/src/__support/FPUtil/dyadic_float.h b/libc/src/__support/FPUtil/dyadic_float.h
index 6522c85f73988..d1e97c68ffd68 100644
--- a/libc/src/__support/FPUtil/dyadic_float.h
+++ b/libc/src/__support/FPUtil/dyadic_float.h
@@ -262,7 +262,8 @@ template <size_t Bits> struct DyadicFloat {
sticky = (mantissa & sticky_mask) != 0;
out_mantissa = static_cast<StorageType>(mantissa >> extra_fraction_len);
- // Takes into consideration the explicit bit for number for types like float 80
+ // Takes into consideration the explicit bit for number for types like
+ // float 80
if constexpr (get_fp_type<T>() == FPType::X86_Binary80)
out_mantissa |= FPBits::EXPLICIT_BIT_MASK;
}
>From 191d42636975d075b83f421ceb4503a9b9db2aab Mon Sep 17 00:00:00 2001
From: Sukumarsawant <sawantsukumar at gmail.com>
Date: Thu, 6 Aug 2026 17:25:32 +0530
Subject: [PATCH 12/13] test: limit EXTRA_FRAC_LEN to be non-negative
test
test
nit
revert
revert
only add >0 condition
format
---
libc/src/__support/FPUtil/dyadic_float.h | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/libc/src/__support/FPUtil/dyadic_float.h b/libc/src/__support/FPUtil/dyadic_float.h
index d1e97c68ffd68..8260af3476ce7 100644
--- a/libc/src/__support/FPUtil/dyadic_float.h
+++ b/libc/src/__support/FPUtil/dyadic_float.h
@@ -256,16 +256,14 @@ 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);
- // Takes into consideration the explicit bit for number for types like
- // float 80
- if constexpr (get_fp_type<T>() == FPType::X86_Binary80)
- out_mantissa |= FPBits::EXPLICIT_BIT_MASK;
}
bool lsb = (out_mantissa & 1) != 0;
>From acc96e6e7db00a2bf19f874dc8fbaa777068b16b Mon Sep 17 00:00:00 2001
From: Sukumarsawant <sawantsukumar at gmail.com>
Date: Sat, 8 Aug 2026 12:33:18 +0530
Subject: [PATCH 13/13] add edge cases test
---
.../src/__support/FPUtil/float80_test.cpp | 40 ++++++++++++++++++-
1 file changed, 39 insertions(+), 1 deletion(-)
diff --git a/libc/test/src/__support/FPUtil/float80_test.cpp b/libc/test/src/__support/FPUtil/float80_test.cpp
index 68b38a63c23b4..65a95838cd087 100644
--- a/libc/test/src/__support/FPUtil/float80_test.cpp
+++ b/libc/test/src/__support/FPUtil/float80_test.cpp
@@ -16,7 +16,10 @@ using LIBC_NAMESPACE::Sign;
using LIBC_NAMESPACE::fputil::Float80;
using FPBits = LIBC_NAMESPACE::fputil::FPBits<Float80>;
-TEST(LlvmLibcFloat80Test, temp) { Float80 a(1.0f); }
+TEST(LlvmLibcFloat80Test, temp) {
+ Float80 a(1.0f);
+
+}
TEST(LlvmLibcFloat80Test, IntegerConversion) {
// Float80 to Integer conversion test
@@ -59,6 +62,41 @@ TEST(LlvmLibcFloat80Test, IntegerConversion) {
EXPECT_EQ(LIBC_NAMESPACE::fputil::test_except(FE_INVALID), 0);
}
+#ifdef LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
+TEST(LlvmLibcFloat80Test, randomTest) {
+ using FPBits = LIBC_NAMESPACE::fputil::FPBits<long double>;
+
+ const FPBits::StorageType EDGE_CASES[] = {
+ FPBits::zero(Sign::POS).uintval(),
+ FPBits::zero(Sign::NEG).uintval(),
+ FPBits::inf(Sign::POS).uintval(),
+ FPBits::inf(Sign::NEG).uintval(),
+ FPBits::quiet_nan().uintval(),
+ FPBits::signaling_nan().uintval(),
+ FPBits::min_subnormal(Sign::POS).uintval(),
+ FPBits::min_subnormal(Sign::NEG).uintval(),
+ FPBits::max_subnormal(Sign::POS).uintval(),
+ FPBits::max_subnormal(Sign::NEG).uintval(),
+ FPBits::min_normal(Sign::POS).uintval(),
+ FPBits::min_normal(Sign::NEG).uintval(),
+ FPBits::max_normal(Sign::POS).uintval(),
+ FPBits::max_normal(Sign::NEG).uintval(),
+ FPBits::one(Sign::POS).uintval(),
+ FPBits::one(Sign::NEG).uintval(),
+ };
+
+ for (FPBits::StorageType bits : EDGE_CASES) {
+ long double native = FPBits(bits).get_val();
+ Float80 emulated(native);
+ if (FPBits(bits).is_nan()) {
+ EXPECT_TRUE(FPBits(emulated).is_nan());
+ } else {
+ EXPECT_EQ(FPBits(emulated).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());
More information about the libc-commits
mailing list