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

Simon Tatham via libc-commits libc-commits at lists.llvm.org
Thu Jul 23 06:19:02 PDT 2026


================
@@ -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());
----------------
statham-arm wrote:

Hi @Sukumarsawant ,

This conversion-to-integer function is causing a crash in our downstream Arm Toolchain, because it doesn't have any handling for floats that are too large to fit in the integer type. If the input value is greater than 2^256, then the call to `xd.as_mantissa_type()` will try to shift a 128-bit integer by more than 128 bits, which is undefined behaviour according to the [big_int shift function](https://github.com/llvm/llvm-project/blob/7c445ab1d08597a89a829ff938b55264cc480150/libc/src/__support/big_int.h#L252-L253) and is causing an out-of-bounds array access within that function.

`as_mantissa_type()` doesn't explicitly comment that you're not supposed to pass it a float with a very large exponent, but existing call sites (in binary/decimal conversion) avoid doing so, so I think that was the intention.

What value do you think should be returned in the case of an out-of-range input?

https://github.com/llvm/llvm-project/pull/200565


More information about the libc-commits mailing list