[libc-commits] [libc] f79a888 - [libc] Fix float128-to-integer conversion UB (#211593)

via libc-commits libc-commits at lists.llvm.org
Fri Jul 31 05:58:18 PDT 2026


Author: Zorojuro
Date: 2026-07-31T18:28:13+05:30
New Revision: f79a8888eb831238423362d004739683e2a20d93

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

LOG: [libc] Fix float128-to-integer conversion UB  (#211593)

https://github.com/llvm/llvm-project/pull/200565#discussion_r3643991194
Fixes the recent issue pointed out in the above comment
about undefined behavior for float128 in the case of extremely large
values and extremely small values.

Added: 
    

Modified: 
    libc/src/__support/FPUtil/CMakeLists.txt
    libc/src/__support/FPUtil/dyadic_float.h
    libc/src/__support/FPUtil/float128.h
    libc/test/src/__support/FPUtil/float128_test.cpp

Removed: 
    


################################################################################
diff  --git a/libc/src/__support/FPUtil/CMakeLists.txt b/libc/src/__support/FPUtil/CMakeLists.txt
index 8b6ac8027b52d..4673ef794c80b 100644
--- a/libc/src/__support/FPUtil/CMakeLists.txt
+++ b/libc/src/__support/FPUtil/CMakeLists.txt
@@ -303,6 +303,7 @@ add_header_library(
     .comparison_operations
     .dyadic_float
     libc.hdr.stdint_proxy
+    libc.src.__support.CPP.limits
     libc.src.__support.CPP.type_traits
     libc.src.__support.FPUtil.generic.add_sub
     libc.src.__support.FPUtil.generic.div

diff  --git a/libc/src/__support/FPUtil/dyadic_float.h b/libc/src/__support/FPUtil/dyadic_float.h
index c218632131934..80892701e689a 100644
--- a/libc/src/__support/FPUtil/dyadic_float.h
+++ b/libc/src/__support/FPUtil/dyadic_float.h
@@ -472,7 +472,11 @@ template <size_t Bits> struct DyadicFloat {
       // exponents coming in to this function _shouldn't_ be that large). The
       // result should always end up as a positive size_t.
       size_t shift = -static_cast<size_t>(exponent);
-      new_mant >>= shift;
+      size_t limit = cpp::numeric_limits<MantissaType>::digits;
+      if (shift >= limit)
+        new_mant = 0;
+      else
+        new_mant >>= shift;
     }
 
     if (sign.is_neg()) {

diff  --git a/libc/src/__support/FPUtil/float128.h b/libc/src/__support/FPUtil/float128.h
index b1bcafefd61a4..1dd1abd417737 100644
--- a/libc/src/__support/FPUtil/float128.h
+++ b/libc/src/__support/FPUtil/float128.h
@@ -10,6 +10,7 @@
 #define LLVM_LIBC_SRC___SUPPORT_FPUTIL_FLOAT128_H
 
 #include "hdr/stdint_proxy.h"
+#include "src/__support/CPP/limits.h"
 #include "src/__support/CPP/type_traits.h"
 #include "src/__support/FPUtil/cast.h"
 #include "src/__support/FPUtil/comparison_operations.h"
@@ -69,13 +70,27 @@ struct Float128 {
 
   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<Float128> 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<Float128>::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<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());

diff  --git a/libc/test/src/__support/FPUtil/float128_test.cpp b/libc/test/src/__support/FPUtil/float128_test.cpp
index b4ee0439ba0f5..5ca7e0e5a7d90 100644
--- a/libc/test/src/__support/FPUtil/float128_test.cpp
+++ b/libc/test/src/__support/FPUtil/float128_test.cpp
@@ -80,22 +80,48 @@ TEST(LlvmLibcFloat128Test, IntegerConversion) {
   ASSERT_EQ(static_cast<int>(Float128(-1.9)), -1);
   ASSERT_EQ(static_cast<int>(Float128(1.9f)), 1);
 
-  // Extreme values
+  // Border values
+  LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
   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);
+  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()), 0);
+  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>(Float128(1e300)), INT_MAX);
+  EXPECT_FP_EXCEPTION(FE_INVALID);
+
+  LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
+  ASSERT_EQ(static_cast<int>(Float128(-1e300)), INT_MIN);
+  EXPECT_FP_EXCEPTION(FE_INVALID);
+
+  LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
+  ASSERT_EQ(static_cast<int>(FPBits::inf(Sign::NEG).get_val()), INT_MIN);
   EXPECT_FP_EXCEPTION(FE_INVALID);
 
   LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
-  ASSERT_EQ(static_cast<int>(FPBits::inf().get_val()), 0);
+  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>(Float128(1e-300)), 0);
+  ASSERT_EQ(static_cast<int>(Float128(-1e-300)), 0);
+  ASSERT_EQ(static_cast<int>(Float128(0.5)), 0);
+  ASSERT_EQ(static_cast<int>(Float128(-0.5)), 0);
+  EXPECT_EQ(LIBC_NAMESPACE::fputil::test_except(FE_INVALID), 0);
 }
 
 TEST(LlvmLibcFloat128Test, FromIntegralTypes) {
@@ -106,4 +132,13 @@ TEST(LlvmLibcFloat128Test, FromIntegralTypes) {
   ASSERT_TRUE(Float128(7U) == Float128(7.0f));
   ASSERT_TRUE(Float128(-7LL) == Float128(-7.0));
   ASSERT_TRUE(Float128(123456789LL) == Float128(123456789.0));
+
+  // 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>(Float128(2147483648.0)), INT_MAX);
+  EXPECT_FP_EXCEPTION(FE_INVALID);
+
+  LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
+  ASSERT_EQ(static_cast<unsigned>(Float128(2147483648.0)), 2147483648U);
+  EXPECT_EQ(LIBC_NAMESPACE::fputil::test_except(FE_INVALID), 0);
 }


        


More information about the libc-commits mailing list