return product;
}
- // Overwrites *this with quotient. Returns true on division by zero.
- constexpr bool DivideUnsigned(
- const FixedPoint &divisor, FixedPoint &remainder) {
- remainder.Clear();
+ struct QuotientWithRemainder {
+ FixedPoint quotient, remainder;
+ bool divisionByZero, overflow;
+ };
+ constexpr QuotientWithRemainder DivideUnsigned(
+ const FixedPoint &divisor) const {
if (divisor.IsZero()) {
- *this = MASKR(bits);
- return true;
+ return {MASKR(bits), FixedPoint{}, true, false}; // overflow to max value
}
int bitsDone{LEADZ()};
FixedPoint top{SHIFTL(bitsDone)};
- Clear();
+ FixedPoint quotient, remainder;
for (; bitsDone < bits; ++bitsDone) {
auto doubledTop{top.AddUnsigned(top)};
top = doubledTop.value;
remainder = remainder.AddUnsigned(remainder, doubledTop.carry).value;
bool nextBit{remainder.CompareUnsigned(divisor) != Ordering::Less};
- *this = AddUnsigned(*this, nextBit).value;
+ quotient = quotient.AddUnsigned(quotient, nextBit).value;
if (nextBit) {
remainder = remainder.SubtractSigned(divisor).value;
}
}
- return false;
+ return {quotient, remainder, false, false};
}
- // Overwrites *this with quotient. Returns true on overflow (viz.,
- // the most negative value divided by -1) and on division by zero.
- // A nonzero remainder has the sign of the dividend, i.e., it is
- // the MOD intrinsic (X-INT(X/Y)*Y), not MODULO (below).
+ // A nonzero remainder has the sign of the dividend, i.e., it computes
+ // the MOD intrinsic (X-INT(X/Y)*Y), not MODULO (which is below).
// 8/5 = 1r3; -8/5 = -1r-3; 8/-5 = -1r3; -8/-5 = 1r-3
- constexpr bool DivideSigned(FixedPoint divisor, FixedPoint &remainder) {
+ constexpr QuotientWithRemainder DivideSigned(FixedPoint divisor) const {
bool dividendIsNegative{IsNegative()};
bool negateQuotient{dividendIsNegative};
Ordering divisorOrdering{divisor.CompareToZeroSigned()};
if (negated.overflow) {
// divisor was (and is) the most negative number
if (CompareUnsigned(divisor) == Ordering::Equal) {
- *this = MASKR(1);
- remainder.Clear();
- return bits <= 1; // edge case: 1-bit signed numbers overflow on 1!
+ return {MASKR(1), FixedPoint{}, false, bits <= 1};
} else {
- remainder = *this;
- Clear();
- return false;
+ return {FixedPoint{}, *this, false, false};
}
}
divisor = negated.value;
} else if (divisorOrdering == Ordering::Equal) {
// division by zero
- remainder.Clear();
if (dividendIsNegative) {
- *this = MASKL(1); // most negative signed number
+ return {MASKL(1), FixedPoint{}, true, false};
} else {
- *this = MASKR(bits - 1); // most positive signed number
+ return {MASKR(bits - 1), FixedPoint{}, true, false};
}
- return true;
}
+ FixedPoint dividend{*this};
if (dividendIsNegative) {
auto negated{Negate()};
if (negated.overflow) {
divisor.CompareUnsigned(FixedPoint{std::uint64_t{1}}) ==
Ordering::Equal) {
// most negative number / -1 is the sole overflow case
- remainder.Clear();
- return true;
+ return {*this, FixedPoint{}, false, true};
}
} else {
- *this = negated.value;
+ dividend = negated.value;
}
}
- // Overflow is not possible, and both the dividend (*this) and divisor
+ // Overflow is not possible, and both the dividend and divisor
// are now positive.
- DivideUnsigned(divisor, remainder);
+ QuotientWithRemainder result{dividend.DivideUnsigned(divisor)};
if (negateQuotient) {
- *this = Negate().value;
+ result.quotient = result.quotient.Negate().value;
}
if (dividendIsNegative) {
- remainder = remainder.Negate().value;
+ result.remainder = result.remainder.Negate().value;
}
- return false;
+ return result;
}
- // MODULO intrinsic. Returns true on overflow. Has the sign of
- // the divisor argument.
+ // Result has the sign of the divisor argument.
// 8 mod 5 = 3; -8 mod 5 = 2; 8 mod -5 = -2; -8 mod -5 = -3
- constexpr bool ModuloSigned(const FixedPoint &divisor) {
+ constexpr ValueWithOverflow MODULO(const FixedPoint &divisor) const {
FixedPoint quotient{*this};
bool negativeDivisor{divisor.IsNegative()};
bool distinctSigns{IsNegative() != negativeDivisor};
- bool overflow{quotient.DivideSigned(divisor, *this)};
- if (distinctSigns && !IsZero()) {
- *this = AddUnsigned(divisor).value;
+ QuotientWithRemainder divided{DivideSigned(divisor)};
+ if (distinctSigns && !divided.remainder.IsZero()) {
+ return {divided.remainder.AddUnsigned(divisor).value, divided.overflow};
+ } else {
+ return {divided.remainder, divided.overflow};
}
- return overflow;
}
private:
("%s, x=0x%llx, y=0x%llx", desc, x, y);
MATCH(((sx * sy) >> BITS) & maxUnsignedValue, product.upper.ToUInt64())
("%s, x=0x%llx, y=0x%llx", desc, x, y);
- copy = a;
- FP rem;
- MATCH(y == 0, copy.DivideUnsigned(b, rem))
- ("%s, x=0x%llx, y=0x%llx", desc, x, y);
+ auto quot{a.DivideUnsigned(b)};
+ MATCH(y == 0, quot.divisionByZero)("%s, x=0x%llx, y=0x%llx", desc, x, y);
if (y == 0) {
- MATCH(maxUnsignedValue, copy.ToUInt64())
+ MATCH(maxUnsignedValue, quot.quotient.ToUInt64())
+ ("%s, x=0x%llx, y=0x%llx", desc, x, y);
+ MATCH(0, quot.remainder.ToUInt64())
("%s, x=0x%llx, y=0x%llx", desc, x, y);
- MATCH(0, rem.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
} else {
- MATCH(x / y, copy.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
- MATCH(x % y, rem.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
+ MATCH(x / y, quot.quotient.ToUInt64())
+ ("%s, x=0x%llx, y=0x%llx", desc, x, y);
+ MATCH(x % y, quot.remainder.ToUInt64())
+ ("%s, x=0x%llx, y=0x%llx", desc, x, y);
}
- copy = a;
+ quot = a.DivideSigned(b);
bool badCase{sx == mostNegativeSignedValue &&
((sy == -1 && sx != sy) || (BITS == 1 && sx == sy))};
- MATCH(y == 0 || badCase, copy.DivideSigned(b, rem))
- ("%s, x=0x%llx, y=0x%llx", desc, x, y);
+ MATCH(y == 0, quot.divisionByZero)("%s, x=0x%llx, y=0x%llx", desc, x, y);
+ MATCH(badCase, quot.overflow)("%s, x=0x%llx, y=0x%llx", desc, x, y);
if (y == 0) {
if (sx >= 0) {
- MATCH(maxPositiveSignedValue, copy.ToInt64())
+ MATCH(maxPositiveSignedValue, quot.quotient.ToInt64())
("%s, x=0x%llx, y=0x%llx", desc, x, y);
} else {
- MATCH(mostNegativeSignedValue, copy.ToInt64())
+ MATCH(mostNegativeSignedValue, quot.quotient.ToInt64())
("%s, x=0x%llx, y=0x%llx", desc, x, y);
}
- MATCH(0, rem.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
+ MATCH(0, quot.remainder.ToUInt64())
+ ("%s, x=0x%llx, y=0x%llx", desc, x, y);
} else if (badCase) {
- MATCH(x, copy.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
- MATCH(0, rem.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
+ MATCH(x, quot.quotient.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
+ MATCH(0, quot.remainder.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
} else {
- MATCH(sx / sy, copy.ToInt64())
+ MATCH(sx / sy, quot.quotient.ToInt64())
("%s, x=0x%llx %lld, y=0x%llx %lld; unsigned 0x%llx", desc, x, sx, y,
- sy, copy.ToUInt64());
- MATCH(sx - sy * (sx / sy), rem.ToInt64())
+ sy, quot.quotient.ToUInt64());
+ MATCH(sx - sy * (sx / sy), quot.remainder.ToInt64())
("%s, x=0x%llx, y=0x%llx", desc, x, y);
}
+ // TODO test MODULO
}
}
}