Implement operator*= for QofInt128

This commit is contained in:
John Ralls 2014-11-09 11:55:59 -08:00
parent d4fdd9ef17
commit 1e6855efe5
3 changed files with 126 additions and 0 deletions

View File

@ -210,6 +210,108 @@ QofInt128::operator-= (const QofInt128& b) noexcept
QofInt128&
QofInt128::operator*= (const QofInt128& b) noexcept
{
/* Test for 0 first */
if (isZero() || b.isZero())
{
m_hi = m_lo = 0;
return *this;
}
/* Test for overflow before spending time on the calculation */
if (m_hi && b.m_hi)
{
m_flags |= overflow;
return *this;
}
uint abits {}, bbits {};
uint64_t temp {m_lo};
do
++abits;
while (temp >>= 1);
temp = m_hi;
do
++abits;
while (temp >>= 1);
temp = b.m_lo;
do
++bbits;
while (temp >>= 1);
temp = b.m_hi;
do
++bbits;
while (temp >>= 1);
if (abits + bbits > maxbits)
{
m_flags |= overflow;
return *this;
}
/* Handle the sign; ^ flips if b is negative */
m_flags ^= (b.m_flags & neg);
/* The trivial case */
if (abits + bbits <= legbits)
{
m_lo *= b.m_lo;
return *this;
}
/* This is Knuth's "classical" multi-precision multiplication algorithm
* truncated to a QofInt128 result with the loop unrolled for clarity and with
* overflow and zero checks beforehand to save time. See Donald Knuth, "The Art
* of Computer Programming Volume 2: Seminumerical Algorithms", Addison-Wesley,
* 1998, p 268.
*
* There are potentially faster algorithms (O(n^1.6) instead of O(n^2) for the
* full precision), but this is already close to that fast because of truncation
* and it's not clear that the truncation is applicable to the faster
* algorithms.
*/
static const uint sublegs = numlegs * 2;
static const uint sublegbits = legbits / 2;
static const uint sublegmask = (UINT64_C(1) << sublegbits) - 1;
uint64_t av[sublegs] {(m_lo & sublegmask), (m_lo >> sublegbits),
(m_hi & sublegmask), (m_hi >> sublegbits)};
uint64_t bv[sublegs] {(b.m_lo & sublegmask), (b.m_lo >> sublegbits),
(b.m_hi & sublegmask), (b.m_hi >> sublegbits)};
uint64_t rv[sublegs] {};
int64_t carry {}, scratch {};
rv[0] = av[0] * bv[0];
rv[1] = av[1] * bv [0];
scratch = rv[1] + av[0] * bv[1];
carry = rv[1] > scratch ? 1 : 0;
rv[1] = scratch;
rv[2] = av[2] * bv[0] + carry; //0xffff^2 + 1 < 0xffffffff, can't overflow
scratch = rv[2] + av[1] * bv[1];
carry = rv[2] > scratch ? 1 : 0;
rv[2] = scratch + av[0] * bv[2];
carry += scratch > rv[2] ? 1 : 0;
rv[3] = av[3] * bv[0] + carry;
scratch = rv[3] + av[2] * bv[1];
carry = rv[3] > scratch ? 1 : 0;
rv[3] = scratch + av[1] * bv[2];
carry += scratch > rv[3] ? 1 : 0;
scratch = rv[3] + av[0] * bv[3];
carry += rv[3] > scratch ? 1 : 0;
rv[3] = scratch;
if (carry) //Shouldn't happen because of the checks above
{
m_flags |= overflow;
return *this;
}
m_lo = rv[0] + (rv[1] << sublegbits);
carry = rv[1] >> sublegbits;
carry += (rv[1] << sublegbits) > m_lo || rv[0] > m_lo ? 1 : 0;
m_hi = rv[2] + (rv[3] << sublegbits) + carry;
if ((rv[3] << sublegbits) > m_hi || rv[2] > m_hi || (rv[3] >> sublegbits))
{
m_flags |= overflow;
return *this;
}
return *this;
}

View File

@ -50,6 +50,10 @@ class QofInt128
uint64_t m_lo;
public:
static const uint numlegs = 2;
static const uint legbits = 64;
static const uint maxbits = legbits * numlegs;
enum // Values for m_flags
{
pos = 0,

View File

@ -317,3 +317,23 @@ TEST(qofint128_functions, add_and_subtract)
EXPECT_TRUE (bigger.isOverflow());
}
TEST(qofint128_functions, multiply)
{
int64_t barg {INT64_C(4878849681579065407)};
int64_t sarg {INT64_C(4344522355275710400)};
uint64_t uarg {UINT64_C(13567894392130486208)};
QofInt128 smallest (sarg);
QofInt128 smaller (barg);
QofInt128 small (uarg);
QofInt128 big (sarg, barg);
QofInt128 bigger (static_cast<uint64_t>(barg), uarg);
small *= big;
EXPECT_TRUE (small.isOverflow());
big *= bigger;
EXPECT_TRUE (big.isOverflow());
EXPECT_EQ (QofInt128(UINT64_C(1149052180967758316), UINT64_C(6323251814974894144)), smallest *= smaller);
EXPECT_FALSE (smallest.isOverflow());
}