[llvm] [Support][APInt] Fix sign extension, exponent and mantissa in APInt::roundToDouble (PR #192451)

Max Graey via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 16 23:49:25 PDT 2026


================
@@ -890,66 +891,25 @@ APInt llvm::APIntOps::RoundDoubleToAPInt(double Double, unsigned width) {
   return isNeg ? -Tmp : Tmp;
 }
 
-/// This function converts this APInt to a double.
+/// Converts this APInt to a double with rounding towards zero.
+///
 /// The layout for double is as following (IEEE Standard 754):
 ///  --------------------------------------
 /// |  Sign    Exponent    Fraction    Bias |
 /// |-------------------------------------- |
 /// |  1[63]   11[62-52]   52[51-00]   1023 |
 ///  --------------------------------------
 double APInt::roundToDouble(bool isSigned) const {
-  // Handle the simple case where the value is contained in one uint64_t.
-  // It is wrong to optimize getWord(0) to VAL; there might be more than one word.
-  if (isSingleWord() || getActiveBits() <= APINT_BITS_PER_WORD) {
-    if (isSigned) {
-      int64_t sext = SignExtend64(getWord(0), BitWidth);
-      return double(sext);
-    }
-    return double(getWord(0));
-  }
-
-  // Determine if the value is negative.
-  bool isNeg = isSigned ? (*this)[BitWidth-1] : false;
-
-  // Construct the absolute value if we're negative.
-  APInt Tmp(isNeg ? -(*this) : (*this));
-
-  // Figure out how many bits we're using.
-  unsigned n = Tmp.getActiveBits();
-
-  // The exponent (without bias normalization) is just the number of bits
-  // we are using. Note that the sign bit is gone since we constructed the
-  // absolute value.
-  uint64_t exp = n;
+  // Early exit for zero-length values; APFloat::convertFromAPInt only
+  // applies to values with at least one word.
+  if (BitWidth == 0)
+    return 0.0;
 
-  // Return infinity for exponent overflow
-  if (exp > 1023) {
-    if (!isSigned || !isNeg)
-      return std::numeric_limits<double>::infinity();
-    else
-      return -std::numeric_limits<double>::infinity();
-  }
-  exp += 1023; // Increment for 1023 bias
-
-  // Number of bits in mantissa is 52. To obtain the mantissa value, we must
-  // extract the high 52 bits from the correct words in pVal.
-  uint64_t mantissa;
-  unsigned hiWord = whichWord(n-1);
-  if (hiWord == 0) {
-    mantissa = Tmp.U.pVal[0];
-    if (n > 52)
-      mantissa >>= n - 52; // shift down, we want the top 52 bits.
-  } else {
-    assert(hiWord > 0 && "huh?");
-    uint64_t hibits = Tmp.U.pVal[hiWord] << (52 - n % APINT_BITS_PER_WORD);
-    uint64_t lobits = Tmp.U.pVal[hiWord-1] >> (11 + n % APINT_BITS_PER_WORD);
-    mantissa = hibits | lobits;
-  }
+  APFloat f(APFloat::IEEEdouble());
+  f.convertFromAPInt(*this, isSigned,
+                     llvm::APFloatBase::roundingMode::TowardZero);
----------------
MaxGraey wrote:

I still think it should be round to nearest integer "ties to even" instead "towards zero". Floating point semantics matching IEC 60559 (IEEE 754) are defined in Annex F of the standard, which technically is optional and IB, but in practice many compilers and libraries for targets and runtimes (like CUDA, WebAssembly) use default ties to even during integer -> float point conversion. So I’m genuinely curious as to why this particular rounding method was chosen in the first place

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


More information about the llvm-commits mailing list