From 9d0956ebd47144a8e8ada237aeb4d2e5118422b6 Mon Sep 17 00:00:00 2001 From: Ehud Katz Date: Wed, 12 Feb 2020 10:42:55 +0200 Subject: [PATCH] [APFloat] Fix FP remainder operation Reimplement IEEEFloat::remainder() function. Fix PR3359. Differential Revision: https://reviews.llvm.org/D69776 --- llvm/include/llvm/ADT/APFloat.h | 1 + llvm/lib/Support/APFloat.cpp | 148 ++++++++-- llvm/unittests/ADT/APFloatTest.cpp | 438 +++++++++++++++++++++++++++++ 3 files changed, 566 insertions(+), 21 deletions(-) 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 @@ private: 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. + // - fs = subtract(V, rmNearestTiesToEven); - assert(fs==opOK || fs==opInexact); // likewise + // Extend the semantics to prevent an overflow/underflow or inexact result. + bool losesInfo; + fltSemantics extendedSemantics = *semantics; + extendedSemantics.maxExponent++; + extendedSemantics.minExponent--; + extendedSemantics.precision += 2; + + 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 different 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;