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

via llvm-commits llvm-commits at lists.llvm.org
Sat Jul 4 06:14:18 PDT 2026


Author: janr-bay
Date: 2026-07-04T15:14:13+02:00
New Revision: 72070deb4d811cce31154257a1acfca74d738130

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

LOG: [APFloat] Extend fltSemantics with field for explicit integer bit (#204860)

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.

Added: 
    

Modified: 
    llvm/include/llvm/ADT/APFloat.h
    llvm/lib/Support/APFloat.cpp
    llvm/unittests/ADT/APFloatTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ADT/APFloat.h b/llvm/include/llvm/ADT/APFloat.h
index 91bfc425a0ab0..49141c6de80be 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..94a981016d95b 100644
--- a/llvm/lib/Support/APFloat.cpp
+++ b/llvm/lib/Support/APFloat.cpp
@@ -103,8 +103,18 @@ constexpr fltSemantics APFloatBase::semFloat6E2M3FN = {
     2, 0, 4, 6, fltNonfiniteBehavior::FiniteOnly};
 constexpr fltSemantics APFloatBase::semFloat4E2M1FN = {
     2, 0, 2, 4, fltNonfiniteBehavior::FiniteOnly};
-constexpr fltSemantics APFloatBase::semX87DoubleExtended = {16383, -16382, 64,
-                                                            80};
+constexpr fltSemantics APFloatBase::semX87DoubleExtended = {
+    16383,
+    -16382,
+    64,
+    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 +3680,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 +3720,16 @@ 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 + 1);
   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 +3740,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 +3769,23 @@ 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 == ::exponentInf(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;
     }
@@ -3801,7 +3794,30 @@ void IEEEFloat::initFromIEEEAPInt(const APInt &api) {
   bool is_nan = false;
 
   if constexpr (S.nanEncoding == fltNanEncoding::IEEE) {
-    is_nan = myexponent - bias == ::exponentNaN(S) && !all_zero_significand;
+    if constexpr (S.hasExplicitIntegerBit) {
+      // This is only used and tested for x87DoubleExtended
+      static_assert(S.precision == 64);
+      const integerPart myintegerbit =
+          mysignificand[0] >> (trailing_significand_bits - 1);
+      constexpr integerPart significand_mask_no_int_bit =
+          (uint64_t{1} << (trailing_significand_bits - 1)) - 1;
+
+      if (myexponent - bias == ::exponentNaN(S) &&
+          (mysignificand[0] & significand_mask_no_int_bit) != 0) {
+        // regular NaN and pseudoNaN
+        is_nan = true;
+      } else if (myexponent - bias == ::exponentNaN(S) &&
+                 (mysignificand[0] & significand_mask_no_int_bit) == 0) {
+        // pseudoinfinity
+        is_nan = true;
+      } else if (myexponent - bias != ::exponentNaN(S) && myexponent != 0 &&
+                 myintegerbit == 0) {
+        // unnormal
+        is_nan = true;
+      }
+    } else {
+      is_nan = myexponent - bias == ::exponentNaN(S) && !all_zero_significand;
+    }
   } else if constexpr (S.nanEncoding == fltNanEncoding::AllOnes) {
     bool all_ones_significand =
         std::all_of(mysignificand.begin(), mysignificand.end() - 1,
@@ -3831,8 +3847,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 8d8ba27d87a4c..bdf36b906f5be 100644
--- a/llvm/unittests/ADT/APFloatTest.cpp
+++ b/llvm/unittests/ADT/APFloatTest.cpp
@@ -7383,6 +7383,105 @@ 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, (1ull << 14ull) - 1ull, 1, (1ull << 63ull) - 1ull)
+          .bitcastToAPInt(),
+      (APInt(80, 1) << 78) - 1);
+
+  const fltSemantics &S = APFloat::x87DoubleExtended();
+
+  // Test valid infinity: exp=0x7fff, int_bit=1, significand=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, significand!=0
+  {
+    APFloat nan(S, APInt(80, {0xC000000000000000ull, 0x7fffull}));
+    EXPECT_TRUE(nan.isNaN());
+    EXPECT_FALSE(nan.isInfinity());
+  }
+
+  // Test pseudoinfinity: exp=0x7fff, int_bit=0, significand=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, significand!=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, 0));
+    EXPECT_TRUE(pseudoDenormal.isFinite());
+    EXPECT_FALSE(pseudoDenormal.isDenormal());
+    APFloat scale(APFloat::x87DoubleExtended(),
+                  makeX87Bits(false, bias * 2 - 1, 1, 0));
+    EXPECT_TRUE((pseudoDenormal * scale).bitwiseIsEqual(makeX87(1.0)));
+  }
+}
+
 static bool isBitcastRoundtripSafe(APFloat value) {
   APInt bits = value.bitcastToAPInt();
   APFloat fromBits = APFloat(value.getSemantics(), bits);


        


More information about the llvm-commits mailing list