[llvm] 9d0956e - [APFloat] Fix FP remainder operation

Ehud Katz via llvm-commits llvm-commits at lists.llvm.org
Wed Feb 12 00:44:54 PST 2020


Author: Ehud Katz
Date: 2020-02-12T10:42:55+02:00
New Revision: 9d0956ebd47144a8e8ada237aeb4d2e5118422b6

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

LOG: [APFloat] Fix FP remainder operation

Reimplement IEEEFloat::remainder() function.

Fix PR3359.

Differential Revision: https://reviews.llvm.org/D69776

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 0b744267a9ad..ce8fd6536b4b 100644
--- a/llvm/include/llvm/ADT/APFloat.h
+++ b/llvm/include/llvm/ADT/APFloat.h
@@ -511,6 +511,7 @@ class IEEEFloat final : public APFloatBase {
   opStatus divideSpecials(const IEEEFloat &);
   opStatus multiplySpecials(const IEEEFloat &);
   opStatus modSpecials(const IEEEFloat &);
+  opStatus remainderSpecials(const IEEEFloat&);
 
   /// @}
 

diff  --git a/llvm/lib/Support/APFloat.cpp b/llvm/lib/Support/APFloat.cpp
index 03f0bb1f705e..54f8f8e4bfdb 100644
--- a/llvm/lib/Support/APFloat.cpp
+++ b/llvm/lib/Support/APFloat.cpp
@@ -1686,6 +1686,44 @@ IEEEFloat::opStatus IEEEFloat::modSpecials(const IEEEFloat &rhs) {
   }
 }
 
+IEEEFloat::opStatus IEEEFloat::remainderSpecials(const IEEEFloat &rhs) {
+  switch (PackCategoriesIntoKey(category, rhs.category)) {
+  default:
+    llvm_unreachable(nullptr);
+
+  case PackCategoriesIntoKey(fcZero, fcNaN):
+  case PackCategoriesIntoKey(fcNormal, fcNaN):
+  case PackCategoriesIntoKey(fcInfinity, fcNaN):
+    assign(rhs);
+    LLVM_FALLTHROUGH;
+  case PackCategoriesIntoKey(fcNaN, fcZero):
+  case PackCategoriesIntoKey(fcNaN, fcNormal):
+  case PackCategoriesIntoKey(fcNaN, fcInfinity):
+  case PackCategoriesIntoKey(fcNaN, fcNaN):
+    if (isSignaling()) {
+      makeQuiet();
+      return opInvalidOp;
+    }
+    return rhs.isSignaling() ? opInvalidOp : opOK;
+
+  case PackCategoriesIntoKey(fcZero, fcInfinity):
+  case PackCategoriesIntoKey(fcZero, fcNormal):
+  case PackCategoriesIntoKey(fcNormal, fcInfinity):
+    return opOK;
+
+  case PackCategoriesIntoKey(fcNormal, fcZero):
+  case PackCategoriesIntoKey(fcInfinity, fcZero):
+  case PackCategoriesIntoKey(fcInfinity, fcNormal):
+  case PackCategoriesIntoKey(fcInfinity, fcInfinity):
+  case PackCategoriesIntoKey(fcZero, fcZero):
+    makeNaN();
+    return opInvalidOp;
+
+  case PackCategoriesIntoKey(fcNormal, fcNormal):
+    return opDivByZero; // fake status, indicating this is not a special case
+  }
+}
+
 /* Change sign.  */
 void IEEEFloat::changeSign() {
   /* Look mummy, this one's easy.  */
@@ -1770,40 +1808,108 @@ IEEEFloat::opStatus IEEEFloat::divide(const IEEEFloat &rhs,
   return fs;
 }
 
-/* Normalized remainder.  This is not currently correct in all cases.  */
+/* Normalized remainder.  */
 IEEEFloat::opStatus IEEEFloat::remainder(const IEEEFloat &rhs) {
   opStatus fs;
-  IEEEFloat V = *this;
   unsigned int origSign = sign;
 
-  fs = V.divide(rhs, rmNearestTiesToEven);
-  if (fs == opDivByZero)
+  // First handle the special cases.
+  fs = remainderSpecials(rhs);
+  if (fs != opDivByZero)
     return fs;
 
-  int parts = partCount();
-  integerPart *x = new integerPart[parts];
-  bool ignored;
-  fs = V.convertToInteger(makeMutableArrayRef(x, parts),
-                          parts * integerPartWidth, true, rmNearestTiesToEven,
-                          &ignored);
-  if (fs == opInvalidOp) {
-    delete[] x;
-    return fs;
+  fs = opOK;
+
+  // Make sure the current value is less than twice the denom. If the addition
+  // did not succeed (an overflow has happened), which means that the finite
+  // value we currently posses must be less than twice the denom (as we are
+  // using the same semantics).
+  IEEEFloat P2 = rhs;
+  if (P2.add(rhs, rmNearestTiesToEven) == opOK) {
+    fs = mod(P2);
+    assert(fs == opOK);
   }
 
-  fs = V.convertFromZeroExtendedInteger(x, parts * integerPartWidth, true,
-                                        rmNearestTiesToEven);
-  assert(fs==opOK);   // should always work
+  // Lets work with absolute numbers.
+  IEEEFloat P = rhs;
+  P.sign = false;
+  sign = false;
 
-  fs = V.multiply(rhs, rmNearestTiesToEven);
-  assert(fs==opOK || fs==opInexact);   // should not overflow or underflow
+  //
+  // To calculate the remainder we use the following scheme.
+  //
+  // The remainder is defained as follows:
+  //
+  // remainder = numer - rquot * denom = x - r * p
+  //
+  // Where r is the result of: x/p, rounded toward the nearest integral value
+  // (with halfway cases rounded toward the even number).
+  //
+  // Currently, (after x mod 2p):
+  // r is the number of 2p's present inside x, which is inherently, an even
+  // number of p's.
+  //
+  // We may split the remaining calculation into 4 options:
+  // - if x < 0.5p then we round to the nearest number with is 0, and are done.
+  // - if x == 0.5p then we round to the nearest even number which is 0, and we
+  //   are done as well.
+  // - if 0.5p < x < p then we round to nearest number which is 1, and we have
+  //   to subtract 1p at least once.
+  // - if x >= p then we must subtract p at least once, as x must be a
+  //   remainder.
+  //
+  // By now, we were done, or we added 1 to r, which in turn, now an odd number.
+  //
+  // We can now split the remaining calculation to the following 3 options:
+  // - if x < 0.5p then we round to the nearest number with is 0, and are done.
+  // - if x == 0.5p then we round to the nearest even number. As r is odd, we
+  //   must round up to the next even number. so we must subtract p once more.
+  // - if x > 0.5p (and inherently x < p) then we must round r up to the next
+  //   integral, and subtract p once more.
+  //
+
+  // Extend the semantics to prevent an overflow/underflow or inexact result.
+  bool losesInfo;
+  fltSemantics extendedSemantics = *semantics;
+  extendedSemantics.maxExponent++;
+  extendedSemantics.minExponent--;
+  extendedSemantics.precision += 2;
 
-  fs = subtract(V, rmNearestTiesToEven);
-  assert(fs==opOK || fs==opInexact);   // likewise
+  IEEEFloat VEx = *this;
+  fs = VEx.convert(extendedSemantics, rmNearestTiesToEven, &losesInfo);
+  assert(fs == opOK && !losesInfo);
+  IEEEFloat PEx = P;
+  fs = PEx.convert(extendedSemantics, rmNearestTiesToEven, &losesInfo);
+  assert(fs == opOK && !losesInfo);
+
+  // It is simpler to work with 2x instead of 0.5p, and we do not need to lose
+  // any fraction.
+  fs = VEx.add(VEx, rmNearestTiesToEven);
+  assert(fs == opOK);
+
+  if (VEx.compare(PEx) == cmpGreaterThan) {
+    fs = subtract(P, rmNearestTiesToEven);
+    assert(fs == opOK);
+
+    // Make VEx = this.add(this), but because we have 
diff erent semantics, we do
+    // not want to `convert` again, so we just subtract PEx twice (which equals
+    // to the desired value).
+    fs = VEx.subtract(PEx, rmNearestTiesToEven);
+    assert(fs == opOK);
+    fs = VEx.subtract(PEx, rmNearestTiesToEven);
+    assert(fs == opOK);
+
+    cmpResult result = VEx.compare(PEx);
+    if (result == cmpGreaterThan || result == cmpEqual) {
+      fs = subtract(P, rmNearestTiesToEven);
+      assert(fs == opOK);
+    }
+  }
 
   if (isZero())
     sign = origSign;    // IEEE754 requires this
-  delete[] x;
+  else
+    sign ^= origSign;
   return fs;
 }
 

diff  --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp
index e571e04614ae..6822161729b2 100644
--- a/llvm/unittests/ADT/APFloatTest.cpp
+++ b/llvm/unittests/ADT/APFloatTest.cpp
@@ -3410,6 +3410,444 @@ TEST(APFloatTest, mod) {
   }
 }
 
+TEST(APFloatTest, remainder) {
+  // Test Special Cases against each other and normal values.
+
+  APFloat PInf = APFloat::getInf(APFloat::IEEEsingle(), false);
+  APFloat MInf = APFloat::getInf(APFloat::IEEEsingle(), true);
+  APFloat PZero = APFloat::getZero(APFloat::IEEEsingle(), false);
+  APFloat MZero = APFloat::getZero(APFloat::IEEEsingle(), true);
+  APFloat QNaN = APFloat::getNaN(APFloat::IEEEsingle(), false);
+  APFloat SNaN = APFloat(APFloat::IEEEsingle(), "snan123");
+  APFloat PNormalValue = APFloat(APFloat::IEEEsingle(), "0x1p+0");
+  APFloat MNormalValue = APFloat(APFloat::IEEEsingle(), "-0x1p+0");
+  APFloat PLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), false);
+  APFloat MLargestValue = APFloat::getLargest(APFloat::IEEEsingle(), true);
+  APFloat PSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), false);
+  APFloat MSmallestValue = APFloat::getSmallest(APFloat::IEEEsingle(), true);
+  APFloat PSmallestNormalized =
+      APFloat::getSmallestNormalized(APFloat::IEEEsingle(), false);
+  APFloat MSmallestNormalized =
+      APFloat::getSmallestNormalized(APFloat::IEEEsingle(), true);
+
+  APFloat PVal1(APFloat::IEEEsingle(), "0x1.fffffep+126");
+  APFloat MVal1(APFloat::IEEEsingle(), "-0x1.fffffep+126");
+  APFloat PVal2(APFloat::IEEEsingle(), "0x1.fffffep-126");
+  APFloat MVal2(APFloat::IEEEsingle(), "-0x1.fffffep-126");
+  APFloat PVal3(APFloat::IEEEsingle(), "0x1p-125");
+  APFloat MVal3(APFloat::IEEEsingle(), "-0x1p-125");
+  APFloat PVal4(APFloat::IEEEsingle(), "0x1p+127");
+  APFloat MVal4(APFloat::IEEEsingle(), "-0x1p+127");
+  APFloat PVal5(APFloat::IEEEsingle(), "1.5");
+  APFloat MVal5(APFloat::IEEEsingle(), "-1.5");
+  APFloat PVal6(APFloat::IEEEsingle(), "1");
+  APFloat MVal6(APFloat::IEEEsingle(), "-1");
+
+  struct {
+    APFloat x;
+    APFloat y;
+    const char *result;
+    int status;
+    int category;
+  } SpecialCaseTests[] = {
+    { PInf, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PInf, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PInf, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PInf, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },
+    { PInf, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PInf, PNormalValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PInf, MNormalValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PInf, PLargestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PInf, MLargestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PInf, PSmallestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PInf, MSmallestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PInf, PSmallestNormalized, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PInf, MSmallestNormalized, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MInf, PInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MInf, MInf, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MInf, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MInf, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MInf, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },
+    { MInf, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MInf, PNormalValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MInf, MNormalValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MInf, PLargestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MInf, MLargestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MInf, PSmallestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MInf, MSmallestValue, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MInf, PSmallestNormalized, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MInf, MSmallestNormalized, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PZero, PInf, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PZero, MInf, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PZero, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PZero, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },
+    { PZero, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PZero, PNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PZero, MNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PZero, PLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PZero, MLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PZero, PSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PZero, MSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PZero, PSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PZero, MSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MZero, PInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MZero, MInf, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MZero, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MZero, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MZero, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },
+    { MZero, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MZero, PNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MZero, MNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MZero, PLargestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MZero, MLargestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MZero, PSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MZero, MSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MZero, PSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MZero, MSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { QNaN, PInf, "nan", APFloat::opOK, APFloat::fcNaN },
+    { QNaN, MInf, "nan", APFloat::opOK, APFloat::fcNaN },
+    { QNaN, PZero, "nan", APFloat::opOK, APFloat::fcNaN },
+    { QNaN, MZero, "nan", APFloat::opOK, APFloat::fcNaN },
+    { QNaN, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },
+    { QNaN, SNaN, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { QNaN, PNormalValue, "nan", APFloat::opOK, APFloat::fcNaN },
+    { QNaN, MNormalValue, "nan", APFloat::opOK, APFloat::fcNaN },
+    { QNaN, PLargestValue, "nan", APFloat::opOK, APFloat::fcNaN },
+    { QNaN, MLargestValue, "nan", APFloat::opOK, APFloat::fcNaN },
+    { QNaN, PSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN },
+    { QNaN, MSmallestValue, "nan", APFloat::opOK, APFloat::fcNaN },
+    { QNaN, PSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN },
+    { QNaN, MSmallestNormalized, "nan", APFloat::opOK, APFloat::fcNaN },
+    { SNaN, PInf, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { SNaN, MInf, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { SNaN, PZero, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { SNaN, MZero, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { SNaN, QNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { SNaN, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { SNaN, PNormalValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { SNaN, MNormalValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { SNaN, PLargestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { SNaN, MLargestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { SNaN, PSmallestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { SNaN, MSmallestValue, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { SNaN, PSmallestNormalized, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { SNaN, MSmallestNormalized, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PNormalValue, PInf, "0x1p+0", APFloat::opOK, APFloat::fcNormal },
+    { PNormalValue, MInf, "0x1p+0", APFloat::opOK, APFloat::fcNormal },
+    { PNormalValue, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PNormalValue, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },
+    { PNormalValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PNormalValue, PNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PNormalValue, MNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PNormalValue, PLargestValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal },
+    { PNormalValue, MLargestValue, "0x1p+0", APFloat::opOK, APFloat::fcNormal },
+    { PNormalValue, PSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PNormalValue, MSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PNormalValue, PSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PNormalValue, MSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MNormalValue, PInf, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },
+    { MNormalValue, MInf, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },
+    { MNormalValue, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MNormalValue, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MNormalValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },
+    { MNormalValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MNormalValue, PNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MNormalValue, MNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MNormalValue, PLargestValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },
+    { MNormalValue, MLargestValue, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },
+    { MNormalValue, PSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MNormalValue, MSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MNormalValue, PSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MNormalValue, MSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PLargestValue, PInf, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },
+    { PLargestValue, MInf, "0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },
+    { PLargestValue, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PLargestValue, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },
+    { PLargestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PLargestValue, PNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PLargestValue, MNormalValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PLargestValue, PLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PLargestValue, MLargestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PLargestValue, PSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PLargestValue, MSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PLargestValue, PSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PLargestValue, MSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MLargestValue, PInf, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },
+    { MLargestValue, MInf, "-0x1.fffffep+127", APFloat::opOK, APFloat::fcNormal },
+    { MLargestValue, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MLargestValue, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MLargestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },
+    { MLargestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MLargestValue, PNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MLargestValue, MNormalValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MLargestValue, PLargestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MLargestValue, MLargestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MLargestValue, PSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MLargestValue, MSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MLargestValue, PSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MLargestValue, MSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PSmallestValue, PInf, "0x1p-149", APFloat::opOK, APFloat::fcNormal },
+    { PSmallestValue, MInf, "0x1p-149", APFloat::opOK, APFloat::fcNormal },
+    { PSmallestValue, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PSmallestValue, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },
+    { PSmallestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PSmallestValue, PNormalValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal },
+    { PSmallestValue, MNormalValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal },
+    { PSmallestValue, PLargestValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal },
+    { PSmallestValue, MLargestValue, "0x1p-149", APFloat::opOK, APFloat::fcNormal },
+    { PSmallestValue, PSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PSmallestValue, MSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PSmallestValue, PSmallestNormalized, "0x1p-149", APFloat::opOK, APFloat::fcNormal },
+    { PSmallestValue, MSmallestNormalized, "0x1p-149", APFloat::opOK, APFloat::fcNormal },
+    { MSmallestValue, PInf, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },
+    { MSmallestValue, MInf, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },
+    { MSmallestValue, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MSmallestValue, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MSmallestValue, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },
+    { MSmallestValue, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MSmallestValue, PNormalValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },
+    { MSmallestValue, MNormalValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },
+    { MSmallestValue, PLargestValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },
+    { MSmallestValue, MLargestValue, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },
+    { MSmallestValue, PSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MSmallestValue, MSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MSmallestValue, PSmallestNormalized, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },
+    { MSmallestValue, MSmallestNormalized, "-0x1p-149", APFloat::opOK, APFloat::fcNormal },
+    { PSmallestNormalized, PInf, "0x1p-126", APFloat::opOK, APFloat::fcNormal },
+    { PSmallestNormalized, MInf, "0x1p-126", APFloat::opOK, APFloat::fcNormal },
+    { PSmallestNormalized, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PSmallestNormalized, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },
+    { PSmallestNormalized, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { PSmallestNormalized, PNormalValue, "0x1p-126", APFloat::opOK, APFloat::fcNormal },
+    { PSmallestNormalized, MNormalValue, "0x1p-126", APFloat::opOK, APFloat::fcNormal },
+    { PSmallestNormalized, PLargestValue, "0x1p-126", APFloat::opOK, APFloat::fcNormal },
+    { PSmallestNormalized, MLargestValue, "0x1p-126", APFloat::opOK, APFloat::fcNormal },
+    { PSmallestNormalized, PSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PSmallestNormalized, MSmallestValue, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PSmallestNormalized, PSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PSmallestNormalized, MSmallestNormalized, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MSmallestNormalized, PInf, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },
+    { MSmallestNormalized, MInf, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },
+    { MSmallestNormalized, PZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MSmallestNormalized, MZero, "nan", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MSmallestNormalized, QNaN, "nan", APFloat::opOK, APFloat::fcNaN },
+    { MSmallestNormalized, SNaN, "nan123", APFloat::opInvalidOp, APFloat::fcNaN },
+    { MSmallestNormalized, PNormalValue, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },
+    { MSmallestNormalized, MNormalValue, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },
+    { MSmallestNormalized, PLargestValue, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },
+    { MSmallestNormalized, MLargestValue, "-0x1p-126", APFloat::opOK, APFloat::fcNormal },
+    { MSmallestNormalized, PSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MSmallestNormalized, MSmallestValue, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MSmallestNormalized, PSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MSmallestNormalized, MSmallestNormalized, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+
+    { PVal1, PVal1, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal1, MVal1, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal1, PVal2, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal1, MVal2, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal1, PVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal1, MVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal1, PVal4, "-0x1p+103", APFloat::opOK, APFloat::fcNormal },
+    { PVal1, MVal4, "-0x1p+103", APFloat::opOK, APFloat::fcNormal },
+    { PVal1, PVal5, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal1, MVal5, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal1, PVal6, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal1, MVal6, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal1, PVal1, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal1, MVal1, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal1, PVal2, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal1, MVal2, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal1, PVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal1, MVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal1, PVal4, "0x1p+103", APFloat::opOK, APFloat::fcNormal },
+    { MVal1, MVal4, "0x1p+103", APFloat::opOK, APFloat::fcNormal },
+    { MVal1, PVal5, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal1, MVal5, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal1, PVal6, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal1, MVal6, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal2, PVal1, "0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },
+    { PVal2, MVal1, "0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },
+    { PVal2, PVal2, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal2, MVal2, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal2, PVal3, "-0x0.000002p-126", APFloat::opOK, APFloat::fcNormal },
+    { PVal2, MVal3, "-0x0.000002p-126", APFloat::opOK, APFloat::fcNormal },
+    { PVal2, PVal4, "0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },
+    { PVal2, MVal4, "0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },
+    { PVal2, PVal5, "0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },
+    { PVal2, MVal5, "0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },
+    { PVal2, PVal6, "0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },
+    { PVal2, MVal6, "0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },
+    { MVal2, PVal1, "-0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },
+    { MVal2, MVal1, "-0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },
+    { MVal2, PVal2, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal2, MVal2, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal2, PVal3, "0x0.000002p-126", APFloat::opOK, APFloat::fcNormal },
+    { MVal2, MVal3, "0x0.000002p-126", APFloat::opOK, APFloat::fcNormal },
+    { MVal2, PVal4, "-0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },
+    { MVal2, MVal4, "-0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },
+    { MVal2, PVal5, "-0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },
+    { MVal2, MVal5, "-0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },
+    { MVal2, PVal6, "-0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },
+    { MVal2, MVal6, "-0x1.fffffep-126", APFloat::opOK, APFloat::fcNormal },
+    { PVal3, PVal1, "0x1p-125", APFloat::opOK, APFloat::fcNormal },
+    { PVal3, MVal1, "0x1p-125", APFloat::opOK, APFloat::fcNormal },
+    { PVal3, PVal2, "0x0.000002p-126", APFloat::opOK, APFloat::fcNormal },
+    { PVal3, MVal2, "0x0.000002p-126", APFloat::opOK, APFloat::fcNormal },
+    { PVal3, PVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal3, MVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal3, PVal4, "0x1p-125", APFloat::opOK, APFloat::fcNormal },
+    { PVal3, MVal4, "0x1p-125", APFloat::opOK, APFloat::fcNormal },
+    { PVal3, PVal5, "0x1p-125", APFloat::opOK, APFloat::fcNormal },
+    { PVal3, MVal5, "0x1p-125", APFloat::opOK, APFloat::fcNormal },
+    { PVal3, PVal6, "0x1p-125", APFloat::opOK, APFloat::fcNormal },
+    { PVal3, MVal6, "0x1p-125", APFloat::opOK, APFloat::fcNormal },
+    { MVal3, PVal1, "-0x1p-125", APFloat::opOK, APFloat::fcNormal },
+    { MVal3, MVal1, "-0x1p-125", APFloat::opOK, APFloat::fcNormal },
+    { MVal3, PVal2, "-0x0.000002p-126", APFloat::opOK, APFloat::fcNormal },
+    { MVal3, MVal2, "-0x0.000002p-126", APFloat::opOK, APFloat::fcNormal },
+    { MVal3, PVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal3, MVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal3, PVal4, "-0x1p-125", APFloat::opOK, APFloat::fcNormal },
+    { MVal3, MVal4, "-0x1p-125", APFloat::opOK, APFloat::fcNormal },
+    { MVal3, PVal5, "-0x1p-125", APFloat::opOK, APFloat::fcNormal },
+    { MVal3, MVal5, "-0x1p-125", APFloat::opOK, APFloat::fcNormal },
+    { MVal3, PVal6, "-0x1p-125", APFloat::opOK, APFloat::fcNormal },
+    { MVal3, MVal6, "-0x1p-125", APFloat::opOK, APFloat::fcNormal },
+    { PVal4, PVal1, "0x1p+103", APFloat::opOK, APFloat::fcNormal },
+    { PVal4, MVal1, "0x1p+103", APFloat::opOK, APFloat::fcNormal },
+    { PVal4, PVal2, "0x0.002p-126", APFloat::opOK, APFloat::fcNormal },
+    { PVal4, MVal2, "0x0.002p-126", APFloat::opOK, APFloat::fcNormal },
+    { PVal4, PVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal4, MVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal4, PVal4, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal4, MVal4, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal4, PVal5, "0.5", APFloat::opOK, APFloat::fcNormal },
+    { PVal4, MVal5, "0.5", APFloat::opOK, APFloat::fcNormal },
+    { PVal4, PVal6, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal4, MVal6, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal4, PVal1, "-0x1p+103", APFloat::opOK, APFloat::fcNormal },
+    { MVal4, MVal1, "-0x1p+103", APFloat::opOK, APFloat::fcNormal },
+    { MVal4, PVal2, "-0x0.002p-126", APFloat::opOK, APFloat::fcNormal },
+    { MVal4, MVal2, "-0x0.002p-126", APFloat::opOK, APFloat::fcNormal },
+    { MVal4, PVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal4, MVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal4, PVal4, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal4, MVal4, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal4, PVal5, "-0.5", APFloat::opOK, APFloat::fcNormal },
+    { MVal4, MVal5, "-0.5", APFloat::opOK, APFloat::fcNormal },
+    { MVal4, PVal6, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal4, MVal6, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal5, PVal1, "1.5", APFloat::opOK, APFloat::fcNormal },
+    { PVal5, MVal1, "1.5", APFloat::opOK, APFloat::fcNormal },
+    { PVal5, PVal2, "0x0.00006p-126", APFloat::opOK, APFloat::fcNormal },
+    { PVal5, MVal2, "0x0.00006p-126", APFloat::opOK, APFloat::fcNormal },
+    { PVal5, PVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal5, MVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal5, PVal4, "1.5", APFloat::opOK, APFloat::fcNormal },
+    { PVal5, MVal4, "1.5", APFloat::opOK, APFloat::fcNormal },
+    { PVal5, PVal5, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal5, MVal5, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal5, PVal6, "-0.5", APFloat::opOK, APFloat::fcNormal },
+    { PVal5, MVal6, "-0.5", APFloat::opOK, APFloat::fcNormal },
+    { MVal5, PVal1, "-1.5", APFloat::opOK, APFloat::fcNormal },
+    { MVal5, MVal1, "-1.5", APFloat::opOK, APFloat::fcNormal },
+    { MVal5, PVal2, "-0x0.00006p-126", APFloat::opOK, APFloat::fcNormal },
+    { MVal5, MVal2, "-0x0.00006p-126", APFloat::opOK, APFloat::fcNormal },
+    { MVal5, PVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal5, MVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal5, PVal4, "-1.5", APFloat::opOK, APFloat::fcNormal },
+    { MVal5, MVal4, "-1.5", APFloat::opOK, APFloat::fcNormal },
+    { MVal5, PVal5, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal5, MVal5, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal5, PVal6, "0.5", APFloat::opOK, APFloat::fcNormal },
+    { MVal5, MVal6, "0.5", APFloat::opOK, APFloat::fcNormal },
+    { PVal6, PVal1, "0x1p+0", APFloat::opOK, APFloat::fcNormal },
+    { PVal6, MVal1, "0x1p+0", APFloat::opOK, APFloat::fcNormal },
+    { PVal6, PVal2, "0x0.00004p-126", APFloat::opOK, APFloat::fcNormal },
+    { PVal6, MVal2, "0x0.00004p-126", APFloat::opOK, APFloat::fcNormal },
+    { PVal6, PVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal6, MVal3, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal6, PVal4, "0x1p+0", APFloat::opOK, APFloat::fcNormal },
+    { PVal6, MVal4, "0x1p+0", APFloat::opOK, APFloat::fcNormal },
+    { PVal6, PVal5, "-0.5", APFloat::opOK, APFloat::fcNormal },
+    { PVal6, MVal5, "-0.5", APFloat::opOK, APFloat::fcNormal },
+    { PVal6, PVal6, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { PVal6, MVal6, "0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal6, PVal1, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },
+    { MVal6, MVal1, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },
+    { MVal6, PVal2, "-0x0.00004p-126", APFloat::opOK, APFloat::fcNormal },
+    { MVal6, MVal2, "-0x0.00004p-126", APFloat::opOK, APFloat::fcNormal },
+    { MVal6, PVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal6, MVal3, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal6, PVal4, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },
+    { MVal6, MVal4, "-0x1p+0", APFloat::opOK, APFloat::fcNormal },
+    { MVal6, PVal5, "0.5", APFloat::opOK, APFloat::fcNormal },
+    { MVal6, MVal5, "0.5", APFloat::opOK, APFloat::fcNormal },
+    { MVal6, PVal6, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+    { MVal6, MVal6, "-0x0p+0", APFloat::opOK, APFloat::fcZero },
+  };
+
+  for (size_t i = 0; i < array_lengthof(SpecialCaseTests); ++i) {
+    APFloat x(SpecialCaseTests[i].x);
+    APFloat y(SpecialCaseTests[i].y);
+    APFloat::opStatus status = x.remainder(y);
+
+    APFloat result(x.getSemantics(), SpecialCaseTests[i].result);
+
+    EXPECT_TRUE(result.bitwiseIsEqual(x));
+    EXPECT_EQ(SpecialCaseTests[i].status, (int)status);
+    EXPECT_EQ(SpecialCaseTests[i].category, (int)x.getCategory());
+  }
+
+  {
+    APFloat f1(APFloat::IEEEdouble(), "0x1.3333333333333p-2"); // 0.3
+    APFloat f2(APFloat::IEEEdouble(), "0x1.47ae147ae147bp-7"); // 0.01
+    APFloat expected(APFloat::IEEEdouble(), "-0x1.4p-56");
+    EXPECT_EQ(APFloat::opOK, f1.remainder(f2));
+    EXPECT_TRUE(f1.bitwiseIsEqual(expected));
+  }
+  {
+    APFloat f1(APFloat::IEEEdouble(), "0x1p64"); // 1.8446744073709552e19
+    APFloat f2(APFloat::IEEEdouble(), "1.5");
+    APFloat expected(APFloat::IEEEdouble(), "-0.5");
+    EXPECT_EQ(APFloat::opOK, f1.remainder(f2));
+    EXPECT_TRUE(f1.bitwiseIsEqual(expected));
+  }
+  {
+    APFloat f1(APFloat::IEEEdouble(), "0x1p1000");
+    APFloat f2(APFloat::IEEEdouble(), "0x1p-1000");
+    APFloat expected(APFloat::IEEEdouble(), "0.0");
+    EXPECT_EQ(APFloat::opOK, f1.remainder(f2));
+    EXPECT_TRUE(f1.bitwiseIsEqual(expected));
+  }
+  {
+    APFloat f1 = APFloat::getInf(APFloat::IEEEdouble(), false);
+    APFloat f2(APFloat::IEEEdouble(), "1.0");
+    EXPECT_EQ(f1.remainder(f2), APFloat::opInvalidOp);
+    EXPECT_TRUE(f1.isNaN());
+  }
+  {
+    APFloat f1(APFloat::IEEEdouble(), "-4.0");
+    APFloat f2(APFloat::IEEEdouble(), "-2.0");
+    APFloat expected(APFloat::IEEEdouble(), "-0.0");
+    EXPECT_EQ(APFloat::opOK, f1.remainder(f2));
+    EXPECT_TRUE(f1.bitwiseIsEqual(expected));
+  }
+  {
+    APFloat f1(APFloat::IEEEdouble(), "-4.0");
+    APFloat f2(APFloat::IEEEdouble(), "2.0");
+    APFloat expected(APFloat::IEEEdouble(), "-0.0");
+    EXPECT_EQ(APFloat::opOK, f1.remainder(f2));
+    EXPECT_TRUE(f1.bitwiseIsEqual(expected));
+  }
+}
+
 TEST(APFloatTest, PPCDoubleDoubleAddSpecial) {
   using DataType = std::tuple<uint64_t, uint64_t, uint64_t, uint64_t,
                               APFloat::fltCategory, APFloat::roundingMode>;


        


More information about the llvm-commits mailing list