[libc-commits] [libc] 7c445ab - [libc] Float128 Emulation in LLVM libc (#200565)

via libc-commits libc-commits at lists.llvm.org
Tue Jul 21 06:20:31 PDT 2026


Author: Zorojuro
Date: 2026-07-21T18:50:26+05:30
New Revision: 7c445ab1d08597a89a829ff938b55264cc480150

URL: https://github.com/llvm/llvm-project/commit/7c445ab1d08597a89a829ff938b55264cc480150
DIFF: https://github.com/llvm/llvm-project/commit/7c445ab1d08597a89a829ff938b55264cc480150.diff

LOG: [libc] Float128 Emulation in LLVM libc (#200565)

Introducing emulated float128 in LLVM libc

Added: 
    libc/src/__support/FPUtil/float128.h
    libc/test/src/__support/FPUtil/float128_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/FPUtil/generic/add_sub.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 9dc77ad7ee0ea..283c4fcefb6fe 100644
--- a/libc/src/__support/CPP/type_traits/is_floating_point.h
+++ b/libc/src/__support/CPP/type_traits/is_floating_point.h
@@ -30,6 +30,10 @@ template <typename T> struct is_floating_point {
       __is_unqualified_any_of<T, float, double, long double
 #ifdef LIBC_TYPES_HAS_FLOAT16
                               ,
+                              // TODO: Remove this once all functions are
+                              // modified to use the emulated float128 instead
+                              // of the alias to either built-in or emulated
+                              // float128
                               float16
 #endif
 #ifdef LIBC_TYPES_HAS_FLOAT128
@@ -37,7 +41,10 @@ template <typename T> struct is_floating_point {
                               float128
 #endif
                               ,
-                              bfloat16>();
+                              bfloat16
+
+                              ,
+                              fputil::Float128>();
 };
 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 d45dd82560788..8b6ac8027b52d 100644
--- a/libc/src/__support/FPUtil/CMakeLists.txt
+++ b/libc/src/__support/FPUtil/CMakeLists.txt
@@ -294,4 +294,22 @@ add_header_library(
     libc.src.__support.macros.properties.types
 )
 
+add_header_library(
+  float128
+  HDRS
+    float128.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 c52699e17e225..83219b7573f46 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -813,6 +813,8 @@ template <typename T> LIBC_INLINE static constexpr FPType get_fp_type() {
 #endif
   else if constexpr (cpp::is_same_v<UnqualT, bfloat16>)
     return FPType::BFloat16;
+  else if constexpr (cpp::is_same_v<UnqualT, Float128>)
+    return FPType::IEEE754_Binary128;
   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 54c80e862523a..4fc5ea0893ebe 100644
--- a/libc/src/__support/FPUtil/cast.h
+++ b/libc/src/__support/FPUtil/cast.h
@@ -31,7 +31,9 @@ cast(InType x) {
     return x;
   } else {
     if constexpr (cpp::is_same_v<OutType, bfloat16> ||
-                  cpp::is_same_v<InType, bfloat16>
+                  cpp::is_same_v<InType, bfloat16> ||
+                  cpp::is_same_v<OutType, Float128> ||
+                  cpp::is_same_v<InType, Float128>
 #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 f1ac39e97c52b..c218632131934 100644
--- a/libc/src/__support/FPUtil/dyadic_float.h
+++ b/libc/src/__support/FPUtil/dyadic_float.h
@@ -438,7 +438,7 @@ 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>
+    if constexpr (cpp::is_same_v<T, bfloat16> || cpp::is_same_v<T, Float128>
 #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/float128.h b/libc/src/__support/FPUtil/float128.h
new file mode 100644
index 0000000000000..b1bcafefd61a4
--- /dev/null
+++ b/libc/src/__support/FPUtil/float128.h
@@ -0,0 +1,161 @@
+//===-- Definition for Float128 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_FLOAT128_H
+#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_FLOAT128_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 Float128 {
+  UInt128 bits;
+
+  LIBC_INLINE Float128() = default;
+  LIBC_INLINE constexpr Float128(const Float128 &) = default;
+  LIBC_INLINE constexpr Float128(Float128 &&) = default;
+  LIBC_INLINE constexpr Float128 &operator=(const Float128 &) = default;
+  LIBC_INLINE constexpr Float128 &operator=(Float128 &&) = default;
+
+  // Floating point type and integer type
+  template <typename T>
+  LIBC_INLINE constexpr explicit Float128(T value) : bits(0U) {
+    if constexpr (cpp::is_floating_point_v<T>) {
+      bits = fputil::cast<Float128>(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<Float128>::STORAGE_LEN> xd(sign, 0,
+                                                            unsigned_value);
+      bits = xd.template as<Float128, /*ShouldSignalExceptions=*/true>().bits;
+
+    } else if constexpr (cpp::is_convertible_v<T, Float128>) {
+      bits = value.operator Float128().bits;
+    } else {
+      bits = fputil::cast<Float128>(static_cast<float>(value)).bits;
+    }
+  }
+
+  template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T> &&
+                                             !cpp::is_same_v<T, Float128>,
+                                         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<Float128> 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<Float128>::FRACTION_LEN;
+    // sign * 2^(exp-bias) * mantissa
+    DyadicFloat<FPBits<Float128>::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 Float128 operator-() const {
+    fputil::FPBits<Float128> result(*this);
+    result.set_sign(result.is_pos() ? Sign::NEG : Sign::POS);
+    return result.get_val();
+  }
+  // operator overloads
+  LIBC_INLINE constexpr Float128 operator+(const Float128 &other) const {
+    return fputil::generic::add<Float128>(*this, other);
+  }
+
+  LIBC_INLINE constexpr Float128 operator-(const Float128 &other) const {
+    return fputil::generic::sub<Float128>(*this, other);
+  }
+
+  LIBC_INLINE constexpr Float128 operator*(const Float128 &other) const {
+    return fputil::generic::mul<Float128>(*this, other);
+  }
+
+  LIBC_INLINE constexpr Float128 operator/(const Float128 &other) const {
+    return fputil::generic::div<Float128>(*this, other);
+  }
+
+  LIBC_INLINE constexpr Float128 &operator*=(const Float128 &other) {
+    *this = *this * other;
+    return *this;
+  }
+
+  LIBC_INLINE constexpr Float128 &operator+=(const Float128 &other) {
+    *this = *this + other;
+    return *this;
+  }
+
+  LIBC_INLINE constexpr Float128 &operator-=(const Float128 &other) {
+    *this = *this - other;
+    return *this;
+  }
+
+  LIBC_INLINE constexpr Float128 &operator/=(const Float128 &other) {
+    *this = *this / other;
+    return *this;
+  }
+
+  LIBC_INLINE constexpr bool operator==(const Float128 &other) const {
+    return fputil::equals(*this, other);
+  }
+
+  LIBC_INLINE constexpr bool operator!=(const Float128 &other) const {
+    return !fputil::equals(*this, other);
+  }
+
+  LIBC_INLINE constexpr bool operator<(const Float128 &other) const {
+    return fputil::less_than(*this, other);
+  }
+
+  LIBC_INLINE constexpr bool operator<=(const Float128 &other) const {
+    return fputil::less_than_or_equals(*this, other);
+  }
+
+  LIBC_INLINE constexpr bool operator>(const Float128 &other) const {
+    return fputil::greater_than(*this, other);
+  }
+
+  LIBC_INLINE constexpr bool operator>=(const Float128 &other) const {
+    return fputil::greater_than_or_equals(*this, other);
+  }
+};
+
+static_assert(LIBC_NAMESPACE::cpp::is_trivially_constructible<
+              LIBC_NAMESPACE::fputil::Float128>::value);
+static_assert(LIBC_NAMESPACE::cpp::is_trivially_copyable<
+              LIBC_NAMESPACE::fputil::Float128>::value);
+
+} // namespace fputil
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_FLOAT128_H

diff  --git a/libc/src/__support/FPUtil/generic/add_sub.h b/libc/src/__support/FPUtil/generic/add_sub.h
index cf662d051c162..7f6a6a97877ee 100644
--- a/libc/src/__support/FPUtil/generic/add_sub.h
+++ b/libc/src/__support/FPUtil/generic/add_sub.h
@@ -110,8 +110,7 @@ add_or_sub(InType x, InType y) {
 #endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
       }
 
-      if constexpr (cpp::is_same_v<InType, bfloat16> &&
-                    cpp::is_same_v<OutType, bfloat16>) {
+      if constexpr (cpp::is_same_v<InType, OutType>) {
         OutFPBits out_y_bits(y);
         if constexpr (IsSub)
           out_y_bits.set_sign(out_y_bits.sign().negate());

diff  --git a/libc/src/__support/macros/properties/types.h b/libc/src/__support/macros/properties/types.h
index 3259c8a6a1d12..505fa6d7957f8 100644
--- a/libc/src/__support/macros/properties/types.h
+++ b/libc/src/__support/macros/properties/types.h
@@ -58,6 +58,21 @@ using float16 = _Float16;
 // LIBC_TYPES_HAS_FLOAT128 and 'float128' type are provided by
 // "include/llvm-libc-types/float128.h"
 
+// -- Emulated float128 support ------------------------------------------------
+// Float128 is always available regardless of built-in float128 type support in
+// the compiler.
+namespace LIBC_NAMESPACE_DECL {
+namespace fputil {
+struct Float128;
+}
+} // namespace LIBC_NAMESPACE_DECL
+
+// #ifndef LIBC_TYPES_HAS_FLOAT128
+// using float128 = LIBC_NAMESPACE::fputil::Float128;
+// #endif // LIBC_TYPES_HAS_FLOAT128
+// TODO: Commented till we modify all required functions to support emulated
+// 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 81db4ccae44c6..cab95df0e709c 100644
--- a/libc/test/src/__support/FPUtil/CMakeLists.txt
+++ b/libc/test/src/__support/FPUtil/CMakeLists.txt
@@ -39,6 +39,18 @@ add_fp_unittest(
     libc.src.__support.FPUtil.rounding_mode
 )
 
+add_fp_unittest(
+  float128_test
+  SUITE
+    libc-fputil-tests
+  SRCS
+    float128_test.cpp
+  DEPENDS
+    libc.hdr.limits_macros
+    libc.src.__support.FPUtil.fenv_impl
+    libc.src.__support.FPUtil.float128
+)
+
 # 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/float128_test.cpp b/libc/test/src/__support/FPUtil/float128_test.cpp
new file mode 100644
index 0000000000000..b4ee0439ba0f5
--- /dev/null
+++ b/libc/test/src/__support/FPUtil/float128_test.cpp
@@ -0,0 +1,109 @@
+//===-- Unittests for Float128 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/float128.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using LIBC_NAMESPACE::Sign;
+using LIBC_NAMESPACE::fputil::Float128;
+using FPBits = LIBC_NAMESPACE::fputil::FPBits<Float128>;
+
+TEST(LlvmLibcFloat128Test, Operators) {
+  Float128 a(1.0f), b(1.0f), c(2.0f), d(3.0f), pa(1.0f), na(-1.0f);
+
+  // comparison operators
+  ASSERT_TRUE(a == b);
+  ASSERT_TRUE(a == Float128(1.0));
+  ASSERT_TRUE(a != c);
+  ASSERT_TRUE(b != c);
+  ASSERT_TRUE(c > b);
+  ASSERT_TRUE(a >= b);
+  ASSERT_TRUE(b <= c);
+  ASSERT_TRUE(a < c);
+
+  // Unary operators
+  ASSERT_TRUE(-pa == na);
+  ASSERT_TRUE(-(-pa) == pa);
+
+  // Binary operators
+  ASSERT_TRUE((a + b) == c);
+  ASSERT_TRUE((a - b) == Float128(0.0f));
+  ASSERT_TRUE((c * d) == Float128(6.0f));
+  ASSERT_TRUE((Float128(6.0f) / d) == Float128(2.0f));
+
+  // Compound assignment operators
+  a += Float128(1.0f);
+  ASSERT_TRUE(a == c);
+  b -= Float128(1.0f);
+  ASSERT_TRUE(b == Float128(0.0f));
+  c *= Float128(2.0f);
+  ASSERT_TRUE(c == Float128(4.0f));
+  d /= Float128(3.0f);
+  ASSERT_TRUE(d == Float128(1.0f));
+}
+
+TEST(LlvmLibcFloat128Test, SpecialValues) {
+  Float128 inf = FPBits::inf(Sign::POS).get_val();
+  Float128 neg_inf = FPBits::inf(Sign::NEG).get_val();
+  Float128 nan = FPBits::quiet_nan().get_val();
+
+  // checking operators with special values
+  ASSERT_TRUE(Float128(0.0f) == Float128(-0.0f)); // +0.0 == -0.0 is true
+  ASSERT_TRUE(Float128(0.0f) == Float128(0.0f));
+  ASSERT_TRUE(inf == inf);
+  ASSERT_TRUE(-inf == neg_inf);
+  ASSERT_TRUE((inf + Float128(1.0f)) == inf);
+  ASSERT_TRUE(inf + inf == inf);
+  ASSERT_TRUE(nan != nan);
+  ASSERT_TRUE(!(nan == nan));
+  ASSERT_TRUE(nan != Float128(0.0f));
+}
+
+TEST(LlvmLibcFloat128Test, IntegerConversion) {
+  // Float128 to Integer conversion test
+  ASSERT_EQ(static_cast<int>(Float128(0.0f)), 0);
+  ASSERT_EQ(static_cast<int>(Float128(-0.0f)), 0);
+  ASSERT_EQ(static_cast<int>(Float128(1.0f)), 1);
+  ASSERT_EQ(static_cast<int>(Float128(-1.0)), -1);
+  ASSERT_EQ(static_cast<long long>(Float128(1000000000.0)),
+            static_cast<long long>(1000000000));
+  ASSERT_EQ(static_cast<unsigned>(Float128(7.0f)), 7U);
+  ASSERT_EQ(static_cast<int>(Float128(-1.5)), -1);
+  ASSERT_EQ(static_cast<int>(Float128(-1.9)), -1);
+  ASSERT_EQ(static_cast<int>(Float128(1.9f)), 1);
+
+  // Extreme values
+  ASSERT_EQ(static_cast<int>(Float128(INT_MAX)), INT_MAX);
+  ASSERT_EQ(static_cast<int>(Float128(INT_MIN)), INT_MIN);
+  ASSERT_EQ(static_cast<long long>(Float128(LLONG_MAX)), LLONG_MAX);
+  ASSERT_EQ(static_cast<long long>(Float128(LLONG_MIN)), LLONG_MIN);
+  ASSERT_EQ(static_cast<unsigned>(Float128(UINT_MAX)), UINT_MAX);
+  ASSERT_EQ(static_cast<unsigned>(Float128(0U)), 0U);
+
+  // FP exceptions
+  LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
+  ASSERT_EQ(static_cast<int>(FPBits::quiet_nan().get_val()), 0);
+  EXPECT_FP_EXCEPTION(FE_INVALID);
+
+  LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
+  ASSERT_EQ(static_cast<int>(FPBits::inf().get_val()), 0);
+  EXPECT_FP_EXCEPTION(FE_INVALID);
+}
+
+TEST(LlvmLibcFloat128Test, FromIntegralTypes) {
+  // Integer to float128 conversion test
+  ASSERT_TRUE(Float128(42) == Float128(42.0f));
+  ASSERT_TRUE(Float128(-42) == Float128(-42.0f));
+  ASSERT_TRUE(Float128(0) == Float128(0.0f));
+  ASSERT_TRUE(Float128(7U) == Float128(7.0f));
+  ASSERT_TRUE(Float128(-7LL) == Float128(-7.0));
+  ASSERT_TRUE(Float128(123456789LL) == Float128(123456789.0));
+}


        


More information about the libc-commits mailing list