Index: llvm/lib/Support/APFloat.cpp =================================================================== --- llvm/lib/Support/APFloat.cpp +++ llvm/lib/Support/APFloat.cpp @@ -1741,40 +1741,70 @@ 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) + fs = modSpecials(rhs); + if (fs != opOK || !isFinite()) 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; + if (rhs.isFinite()) { + 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 + IEEEFloat P = rhs; + P.sign = false; + sign = false; + + if (compare(P) == cmpEqual) { + makeZero(origSign); + return opOK; + } - fs = V.multiply(rhs, rmNearestTiesToEven); - assert(fs==opOK || fs==opInexact); // should not overflow or underflow + const IEEEFloat two(rhs.getSemantics(), 2); + IEEEFloat P2 = P; + if (P2.divide(two, rmNearestTiesToEven) == opOK) { + if (compare(P2) == cmpGreaterThan) { + fs = subtract(P, rmNearestTiesToEven); + assert(fs == opOK); - fs = subtract(V, rmNearestTiesToEven); - assert(fs==opOK || fs==opInexact); // likewise + cmpResult result = compare(P2); + if (result == cmpGreaterThan || result == cmpEqual) { + fs = subtract(P, rmNearestTiesToEven); + assert(fs == opOK); + } + } + } else { + IEEEFloat V2 = *this; + fs = V2.add(*this, rmNearestTiesToEven); + assert(fs == opOK); + + if (V2.compare(P2) == cmpGreaterThan) { + fs = subtract(P, rmNearestTiesToEven); + assert(fs == opOK); + + V2 = *this; + fs = V2.add(*this, rmNearestTiesToEven); + assert(fs == opOK); + + cmpResult result = V2.compare(P2); + 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; } Index: llvm/unittests/ADT/APFloatTest.cpp =================================================================== --- llvm/unittests/ADT/APFloatTest.cpp +++ llvm/unittests/ADT/APFloatTest.cpp @@ -3371,6 +3371,83 @@ } } +TEST(APFloatTest, remainder) { + { + APFloat f1(APFloat::IEEEdouble(), "1.5"); + APFloat f2(APFloat::IEEEdouble(), "1.0"); + APFloat expected(APFloat::IEEEdouble(), "-0.5"); + EXPECT_EQ(f1.remainder(f2), APFloat::opOK); + EXPECT_TRUE(f1.bitwiseIsEqual(expected)); + } + { + APFloat f1(APFloat::IEEEdouble(), "0.5"); + APFloat f2(APFloat::IEEEdouble(), "1.0"); + APFloat expected(APFloat::IEEEdouble(), "0.5"); + EXPECT_EQ(f1.remainder(f2), APFloat::opOK); + EXPECT_TRUE(f1.bitwiseIsEqual(expected)); + } + { + 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(f1.remainder(f2), APFloat::opOK); + 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(f1.remainder(f2), APFloat::opOK); + EXPECT_TRUE(f1.bitwiseIsEqual(expected)); + } + { + APFloat f1(APFloat::IEEEdouble(), "0x1p1000"); + APFloat f2(APFloat::IEEEdouble(), "0x1p-1000"); + APFloat expected(APFloat::IEEEdouble(), "0.0"); + EXPECT_EQ(f1.remainder(f2), APFloat::opOK); + EXPECT_TRUE(f1.bitwiseIsEqual(expected)); + } + { + APFloat f1(APFloat::IEEEdouble(), "0.0"); + APFloat f2(APFloat::IEEEdouble(), "1.0"); + APFloat expected(APFloat::IEEEdouble(), "0.0"); + EXPECT_EQ(f1.remainder(f2), APFloat::opOK); + EXPECT_TRUE(f1.bitwiseIsEqual(expected)); + } + { + APFloat f1(APFloat::IEEEdouble(), "1.0"); + APFloat f2(APFloat::IEEEdouble(), "0.0"); + EXPECT_EQ(f1.remainder(f2), APFloat::opInvalidOp); + EXPECT_TRUE(f1.isNaN()); + } + { + APFloat f1(APFloat::IEEEdouble(), "0.0"); + APFloat f2(APFloat::IEEEdouble(), "0.0"); + EXPECT_EQ(f1.remainder(f2), APFloat::opInvalidOp); + EXPECT_TRUE(f1.isNaN()); + } + { + 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(f1.remainder(f2), APFloat::opOK); + 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(f1.remainder(f2), APFloat::opOK); + EXPECT_TRUE(f1.bitwiseIsEqual(expected)); + } +} + TEST(APFloatTest, PPCDoubleDoubleAddSpecial) { using DataType = std::tuple;