forked from OSchip/llvm-project
Fix APFloat mod (committing for simonbyrne)
The previous version was prone to intermediate rounding or overflow. Differential Revision: https://reviews.llvm.org/D29346 llvm-svn: 299256
This commit is contained in:
parent
34da36e74f
commit
157c86913a
|
@ -1740,44 +1740,20 @@ IEEEFloat::opStatus IEEEFloat::remainder(const IEEEFloat &rhs) {
|
|||
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(makeMutableArrayRef(x, parts),
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -3192,10 +3192,73 @@ TEST(APFloatTest, frexp) {
|
|||
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<uint64_t, uint64_t, uint64_t, uint64_t,
|
||||
APFloat::fltCategory, APFloat::roundingMode>;
|
||||
DataType Data[] = {
|
||||
APFloat::fltCategory, APFloat::roundingMode>; DataType Data[] = {
|
||||
// (1 + 0) + (-1 + 0) = fcZero
|
||||
std::make_tuple(0x3ff0000000000000ull, 0, 0xbff0000000000000ull, 0,
|
||||
APFloat::fcZero, APFloat::rmNearestTiesToEven),
|
||||
|
|
Loading…
Reference in New Issue