Index: lib/Support/APFloat.cpp =================================================================== --- lib/Support/APFloat.cpp +++ lib/Support/APFloat.cpp @@ -1739,43 +1739,20 @@ return fs; } -/* Normalized llvm frem (C fmod). - This is not currently correct in all cases. */ +/* Normalized llvm frem (C fmod). */ IEEEFloat::opStatus IEEEFloat::mod(const IEEEFloat &rhs) { opStatus fs; fs = modSpecials(rhs); - if (isFiniteNonZero() && rhs.isFiniteNonZero()) { - IEEEFloat V = *this; - unsigned int origSign = sign; - - fs = V.divide(rhs, rmNearestTiesToEven); - if (fs == opDivByZero) - return fs; - - int parts = partCount(); - integerPart *x = new integerPart[parts]; - bool ignored; - fs = V.convertToInteger(x, parts * integerPartWidth, true, - rmTowardZero, &ignored); - if (fs==opInvalidOp) { - delete[] x; - return fs; - } - - fs = V.convertFromZeroExtendedInteger(x, parts * integerPartWidth, true, - rmNearestTiesToEven); - assert(fs==opOK); // should always work - - fs = V.multiply(rhs, rmNearestTiesToEven); - assert(fs==opOK || fs==opInexact); // should not overflow or underflow + while (isFiniteNonZero() && rhs.isFiniteNonZero() && + compareAbsoluteValue(rhs) != cmpLessThan) { + IEEEFloat V = scalbn(rhs, ilogb(*this) - ilogb(rhs), rmNearestTiesToEven); + if (compareAbsoluteValue(V) == cmpLessThan) + V = scalbn(V, -1, rmNearestTiesToEven); + V.sign = sign; fs = subtract(V, rmNearestTiesToEven); - assert(fs==opOK || fs==opInexact); // likewise - - if (isZero()) - sign = origSign; // IEEE754 requires this - delete[] x; + assert(fs == opOK); } return fs; } Index: unittests/ADT/APFloatTest.cpp =================================================================== --- unittests/ADT/APFloatTest.cpp +++ unittests/ADT/APFloatTest.cpp @@ -3192,6 +3192,70 @@ EXPECT_TRUE(APFloat(APFloat::IEEEdouble(), "0x1.c60f120d9f87cp-1").bitwiseIsEqual(Frac)); } +TEST(APFloatTest, mod) { + { + APFloat f1(APFloat::IEEEdouble(), "1.5"); + APFloat f2(APFloat::IEEEdouble(), "1.0"); + APFloat expected(APFloat::IEEEdouble(), "0.5"); + EXPECT_EQ(f1.mod(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.mod(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.47ae147ae1471p-7"); // 0.009999999999999983 + EXPECT_EQ(f1.mod(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(), "1.0"); + EXPECT_EQ(f1.mod(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.mod(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.mod(f2), APFloat::opOK); + EXPECT_TRUE(f1.bitwiseIsEqual(expected)); + } + { + APFloat f1(APFloat::IEEEdouble(), "1.0"); + APFloat f2(APFloat::IEEEdouble(), "0.0"); + EXPECT_EQ(f1.mod(f2), APFloat::opInvalidOp); + EXPECT_TRUE(f1.isNaN()); + } + { + APFloat f1(APFloat::IEEEdouble(), "0.0"); + APFloat f2(APFloat::IEEEdouble(), "0.0"); + EXPECT_EQ(f1.mod(f2), APFloat::opInvalidOp); + EXPECT_TRUE(f1.isNaN()); + } + { + APFloat f1 = APFloat::getInf(APFloat::IEEEdouble(), false); + APFloat f2(APFloat::IEEEdouble(), "1.0"); + EXPECT_EQ(f1.mod(f2), APFloat::opInvalidOp); + EXPECT_TRUE(f1.isNaN()); + } +} + TEST(APFloatTest, PPCDoubleDoubleAddSpecial) { using DataType = std::tuple;