[llvm] [APFloat] Extend fltSemantics with field for explicit integer bit (PR #204860)

via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 19 09:29:57 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-support

Author: janr-bay

<details>
<summary>Changes</summary>

This is one more step towards being able to make APFloat extensible with custom formats in the future.

fltSemantics gained a new flag to indicate the presence of an explicit integer bit as specified by the x86 double extended precision format.

The special case implementation for initFromF80LongDoubleAPInt has been merged into initFromIEEEAPInt.

Also adding tests for x87DoubleExtended bit conversion. initFromF80LongDoubleAPInt was called only twice during in all APFloat unit tests together. Testing common cases, boundary conditions, and the special cases documented in IEEEFloat::initFromF80LongDoubleAPInt.

---
Full diff: https://github.com/llvm/llvm-project/pull/204860.diff


3 Files Affected:

- (modified) llvm/include/llvm/ADT/APFloat.h (+15) 
- (modified) llvm/lib/Support/APFloat.cpp (+45-43) 
- (modified) llvm/unittests/ADT/APFloatTest.cpp (+80) 


``````````diff
diff --git a/llvm/include/llvm/ADT/APFloat.h b/llvm/include/llvm/ADT/APFloat.h
index f76c1fd7e04f6..9d3ca7a3a844d 100644
--- a/llvm/include/llvm/ADT/APFloat.h
+++ b/llvm/include/llvm/ADT/APFloat.h
@@ -994,6 +994,7 @@ enum class fltNanEncoding {
   // behavior described in https://arxiv.org/abs/2206.02915 .
   NegativeZero,
 };
+
 /* Represents floating point arithmetic semantics.  */
 struct fltSemantics {
   /* The largest E such that 2^E is representable; this matches the
@@ -1028,6 +1029,20 @@ struct fltSemantics {
      If both hasDenormals and hasZero are false exponent 0 is assumed to be a
      regular exponent instead of being reserved. This changes the bias by +1. */
   bool hasDenormals = true;
+
+  /* Whether the integer bit is explicitly represented between significant and
+     exponent, for example as specified by the x86 double extended precision
+     format.
+
+     For bit patterns designated as undefined under the standard the following
+     conversions will happen when converting from bits. These follow x87
+     behaviour:
+     - exponent = all 1's, integer bit 0, significand 0 ("pseudoinfinity")
+     - exponent = all 1's, integer bit 0, significand nonzero ("pseudoNaN")
+     - exponent!=0 nor all 1's, integer bit 0 ("unnormal")
+     - exponent = 0, integer bit 1 ("pseudodenormal")
+     The first three are treated as NaNs, the last one as Normal */
+  bool hasExplicitIntegerBit = false;
 };
 
 // This is a interface class that is currently forwarding functionalities from
diff --git a/llvm/lib/Support/APFloat.cpp b/llvm/lib/Support/APFloat.cpp
index 0e95e2172f9bc..a36046d8ea354 100644
--- a/llvm/lib/Support/APFloat.cpp
+++ b/llvm/lib/Support/APFloat.cpp
@@ -104,7 +104,11 @@ constexpr fltSemantics APFloatBase::semFloat6E2M3FN = {
 constexpr fltSemantics APFloatBase::semFloat4E2M1FN = {
     2, 0, 2, 4, fltNonfiniteBehavior::FiniteOnly};
 constexpr fltSemantics APFloatBase::semX87DoubleExtended = {16383, -16382, 64,
-                                                            80};
+                                                            80,
+                                                            fltNonfiniteBehavior::IEEE754,
+                                                            fltNanEncoding::IEEE,
+                                                            true,
+                                                            true, true, true, true};
 constexpr fltSemantics APFloatBase::semBogus = {0, 0, 0, 0};
 constexpr fltSemantics APFloatBase::semPPCDoubleDouble = {-1, 0, 0, 128};
 constexpr fltSemantics APFloatBase::semPPCDoubleDoubleLegacy = {
@@ -3670,42 +3674,8 @@ float128 IEEEFloat::convertToQuad() const {
 }
 #endif
 
-/// Integer bit is explicit in this format.  Intel hardware (387 and later)
-/// does not support these bit patterns:
-///  exponent = all 1's, integer bit 0, significand 0 ("pseudoinfinity")
-///  exponent = all 1's, integer bit 0, significand nonzero ("pseudoNaN")
-///  exponent!=0 nor all 1's, integer bit 0 ("unnormal")
-///  exponent = 0, integer bit 1 ("pseudodenormal")
-/// At the moment, the first three are treated as NaNs, the last one as Normal.
 void IEEEFloat::initFromF80LongDoubleAPInt(const APInt &api) {
-  uint64_t i1 = api.getRawData()[0];
-  uint64_t i2 = api.getRawData()[1];
-  uint64_t myexponent = (i2 & 0x7fff);
-  uint64_t mysignificand = i1;
-  uint8_t myintegerbit = mysignificand >> 63;
-
-  initialize(&APFloatBase::semX87DoubleExtended);
-  assert(partCount()==2);
-
-  sign = static_cast<unsigned int>(i2>>15);
-  if (myexponent == 0 && mysignificand == 0) {
-    makeZero(sign);
-  } else if (myexponent==0x7fff && mysignificand==0x8000000000000000ULL) {
-    makeInf(sign);
-  } else if ((myexponent == 0x7fff && mysignificand != 0x8000000000000000ULL) ||
-             (myexponent != 0x7fff && myexponent != 0 && myintegerbit == 0)) {
-    category = fcNaN;
-    exponent = exponentNaN();
-    significandParts()[0] = mysignificand;
-    significandParts()[1] = 0;
-  } else {
-    category = fcNormal;
-    exponent = myexponent - 16383;
-    significandParts()[0] = mysignificand;
-    significandParts()[1] = 0;
-    if (myexponent==0)          // denormal
-      exponent = -16382;
-  }
+  return initFromIEEEAPInt<APFloatBase::semX87DoubleExtended>(api);
 }
 
 void IEEEFloat::initFromPPCDoubleDoubleLegacyAPInt(const APInt &api) {
@@ -3744,15 +3714,15 @@ template <const fltSemantics &S>
 void IEEEFloat::initFromIEEEAPInt(const APInt &api) {
   assert(api.getBitWidth() == S.sizeInBits);
 
-  constexpr unsigned int trailing_significand_bits = S.precision - 1;
+  constexpr unsigned int trailing_significand_bits = S.precision - 1 + S.hasExplicitIntegerBit;
   constexpr integerPart integer_bit =
       integerPart{1} << (trailing_significand_bits % integerPartWidth);
   constexpr uint64_t significand_mask = integer_bit - 1;
   constexpr unsigned int exponent_bits =
       S.sizeInBits - (S.hasSignedRepr ? 1 : 0) - trailing_significand_bits;
-  constexpr unsigned int stored_significand_parts =
-      partCountForBits(trailing_significand_bits);
   static_assert(exponent_bits < 64);
+  constexpr unsigned int stored_significand_parts =
+      partCountForBits(trailing_significand_bits + 2); // TODO: maybe needs +/-1, was S.hasExplicitIntegerBit ? +3 : +2
   constexpr uint64_t exponent_mask = (uint64_t{1} << exponent_bits) - 1;
   constexpr bool is_zero_exp_reserved = S.hasDenormals || S.hasZero;
   constexpr int bias = -(S.minExponent - (is_zero_exp_reserved ? 1 : 0));
@@ -3763,7 +3733,7 @@ void IEEEFloat::initFromIEEEAPInt(const APInt &api) {
   std::array<integerPart, stored_significand_parts> mysignificand;
   if constexpr (has_significand) {
     std::copy_n(api.getRawData(), mysignificand.size(), mysignificand.begin());
-    if constexpr (significand_mask != 0) {
+    if constexpr (significand_mask != 0 || S.precision >= integerPartWidth) {
       mysignificand[mysignificand.size() - 1] &= significand_mask;
     }
   } else {
@@ -3792,7 +3762,22 @@ void IEEEFloat::initFromIEEEAPInt(const APInt &api) {
   bool is_zero = myexponent == 0 && all_zero_significand && S.hasZero;
 
   if constexpr (S.nonFiniteBehavior == fltNonfiniteBehavior::IEEE754) {
-    if (myexponent - bias == ::exponentInf(S) && all_zero_significand) {
+    bool is_inf = false;
+
+    if constexpr (S.hasExplicitIntegerBit) {
+      // This is only used and tested for x87DoubleExtended
+      static_assert(S.precision == 64);
+      constexpr integerPart significand_mask_no_int_bit = (uint64_t{1} << (trailing_significand_bits - 1)) - 1;
+      const integerPart myintegerbit = mysignificand[0] >> (trailing_significand_bits - 1);
+      is_inf = myexponent - bias == ::exponentNaN(S) &&
+                   myintegerbit == 1 &&
+                   (mysignificand[0] & significand_mask_no_int_bit) == 0;
+    }
+    else {
+      is_inf = myexponent - bias == ::exponentInf(S) && all_zero_significand;
+    }
+
+    if (is_inf) {
       makeInf(sign);
       return;
     }
@@ -3813,6 +3798,20 @@ void IEEEFloat::initFromIEEEAPInt(const APInt &api) {
     is_nan = is_zero && sign;
   }
 
+  if constexpr (S.hasExplicitIntegerBit) {
+    // This is only used and tested for x87DoubleExtended
+    static_assert(S.precision == 64);
+    const integerPart myintegerbit = mysignificand[0] >> 63;
+    constexpr integerPart significand_mask_no_int_bit = (uint64_t{1} << (trailing_significand_bits - 1)) - 1;
+    if (myexponent - bias != ::exponentNaN(S) && myexponent != 0 && myintegerbit == 0) {
+      is_nan = true;
+    }
+    else if (myexponent - bias == ::exponentNaN(S) &&
+             (mysignificand[0] & significand_mask_no_int_bit) == 0) {
+        is_nan = true;
+    }
+  }
+
   if (is_nan) {
     category = fcNaN;
     exponent = ::exponentNaN(S);
@@ -3831,8 +3830,11 @@ void IEEEFloat::initFromIEEEAPInt(const APInt &api) {
   std::copy_n(mysignificand.begin(), mysignificand.size(), significandParts());
   if (myexponent == 0 && S.hasDenormals) // denormal
     exponent = S.minExponent;
-  else
-    significandParts()[mysignificand.size()-1] |= integer_bit; // integer bit
+  else {
+    if constexpr (!S.hasExplicitIntegerBit) {
+      significandParts()[mysignificand.size()-1] |= integer_bit;
+    }
+  }
 }
 
 void IEEEFloat::initFromQuadrupleAPInt(const APInt &api) {
diff --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp
index 4814f3d7e8638..08cdbbe42b913 100644
--- a/llvm/unittests/ADT/APFloatTest.cpp
+++ b/llvm/unittests/ADT/APFloatTest.cpp
@@ -7383,6 +7383,86 @@ TEST(APFloatTest, x87Next) {
   EXPECT_TRUE(ilogb(F) == -1);
 }
 
+static APInt makeX87Bits(bool sign, int64_t exponent, int integerBit, int64_t significand) {
+  return (APInt(80, (sign ? 1 : 0)) << 79) | (APInt(80, exponent) << 64) | (APInt(80, integerBit) << 63) | APInt(80, significand);
+}
+
+static APFloat makeX87FromBits(bool sign, int64_t exponent, int integerBit, int64_t significand) {
+  return APFloat(APFloatBase::x87DoubleExtended(), makeX87Bits(sign, exponent, integerBit, significand));
+}
+
+static APFloat makeX87(float value) {
+  bool losesInfo = false;
+  APFloat apf(value);
+  apf.convert(APFloatBase::x87DoubleExtended(), llvm::APFloat::rmNearestTiesToEven, &losesInfo);
+  return apf;
+}
+
+TEST(APFloatTest, x87Bits) {
+  constexpr int bias = 16383;
+  EXPECT_TRUE(makeX87FromBits(false, 0, 0, 0).bitwiseIsEqual(makeX87(0.0)));
+  EXPECT_TRUE(makeX87FromBits(false, bias, 1, 0).bitwiseIsEqual(makeX87(1.0)));
+
+  EXPECT_TRUE(makeX87FromBits(false, bias+1, 1, 0).bitwiseIsEqual(makeX87(2.0)));
+  EXPECT_TRUE(makeX87FromBits(true, bias+1, 1, 0).bitwiseIsEqual(makeX87(-2.0)));
+  EXPECT_TRUE(makeX87FromBits(false, bias+2, 1, 0).bitwiseIsEqual(makeX87(4.0)));
+  EXPECT_TRUE(makeX87FromBits(true, bias+2, 1, 0).bitwiseIsEqual(makeX87(-4.0)));
+  EXPECT_TRUE(makeX87FromBits(false, bias+8, 1, 0).bitwiseIsEqual(makeX87(256.0)));
+  EXPECT_TRUE(makeX87FromBits(true, bias+8, 1, 0).bitwiseIsEqual(makeX87(-256.0)));
+
+  EXPECT_EQ(makeX87FromBits(false, (1u << 14u) - 1u, 1, 0).bitcastToAPInt(), APInt(80, 0x7fff) << 63);
+  EXPECT_EQ(makeX87FromBits(false, (1u << 14u) - 1u, 1, (1ul << 63ul) - 1ul).bitcastToAPInt(), (APInt(80, 1) << 78) - 1);
+
+  const fltSemantics& S = APFloat::x87DoubleExtended();
+
+  // Test valid infinity: exp=0x7FFF, int_bit=1, fraction=0
+  {
+    APFloat inf(S, APInt(80, {0x8000000000000000ULL, 0x7FFFULL}));
+    EXPECT_TRUE(inf.isInfinity());
+    EXPECT_FALSE(inf.isNaN());
+    EXPECT_TRUE(inf.bitwiseIsEqual(APFloat::getInf(S, false)));
+  }
+
+  // Test valid NaN: exp=0x7FFF, int_bit=1, fraction!=0
+  {
+    APFloat nan(S, APInt(80, {0xC000000000000000ULL, 0x7FFFULL}));
+    EXPECT_TRUE(nan.isNaN());
+    EXPECT_FALSE(nan.isInfinity());
+  }
+
+  // Test pseudoinfinity: exp=0x7FFF, int_bit=0, fraction=0
+  // Is treated as NaN in APFloat, see IEEEFloat::initFromF80LongDoubleAPInt
+  {
+    APFloat pseudoInf(S, APInt(80, {0x0000000000000000ULL, 0x7FFFULL}));
+    EXPECT_TRUE(pseudoInf.isNaN());
+    EXPECT_TRUE(pseudoInf.isSignaling());
+    EXPECT_FALSE(pseudoInf.isInfinity());
+  }
+
+  // Test pseudoNaN: exp=0x7FFF, int_bit=0, fraction!=0
+  // Is treated as NaN in APFloat, see IEEEFloat::initFromF80LongDoubleAPInt
+  {
+    APFloat pseudoNan(S, APInt(80, {0x4000000000000000ULL, 0x7FFFULL}));
+    EXPECT_TRUE(pseudoNan.isNaN());
+    EXPECT_FALSE(pseudoNan.isInfinity());
+  }
+
+  // Test unnormal: exp!=0 and !=0x7FFF, int_bit=0
+  // Is treated as NaN in APFloat, see IEEEFloat::initFromF80LongDoubleAPInt
+  {
+    APFloat unnormal(S, APInt(80, {0x4000000000000000ULL, 0x4000ULL}));
+    EXPECT_TRUE(unnormal.isNaN());
+    EXPECT_FALSE(unnormal.isSignaling());
+  }
+
+  // Test pseudodenormal: exp=0, integer int_bit=1
+  {
+    APFloat pseudoDenormal(APFloat::x87DoubleExtended(), makeX87Bits(false, 0, 1, 1));
+    EXPECT_TRUE(pseudoDenormal.isFinite());
+    EXPECT_FALSE(pseudoDenormal.isDenormal());
+  }
+}
+
 static bool isBitcastRoundtripSafe(APFloat value) {
   APInt bits = value.bitcastToAPInt();
   APFloat fromBits = APFloat(value.getSemantics(), bits);

``````````

</details>


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


More information about the llvm-commits mailing list