8

I have a struct representing a nonnegative rational number p/q:

struct rational {
    uint64_t p;
    uint64_t q; // invariant: always > 0
};

I would like to multiply my rational by a uint64 n and get an integer result, rounded down. That is, I would like to calculate:

uint64_t m = (n * r.p)/r.q;

while avoiding intermediate overflow in n * r.p. (Of course the final result may overflow, which is acceptable.)

How can I do this? Is there a way to do it without a high-multiply?

(I looked at boost::rational but it does not appear to provide this feature.)

ridiculous_fish
  • 17,273
  • 1
  • 54
  • 61

2 Answers2

1

You can use peasant multiplication:

// requires 0 < c < 2^63
// returns (a * b) / c.
uint64_t multDiv(uint64_t a, uint64_t b, uint64_t c) {
  uint64_t rem = 0;
  uint64_t res = (a / c) * b;
  a = a % c;
  // invariant: a_orig * b_orig = (res * c + rem) + a * b
  // a < c, rem < c.
  while (b != 0) {
    if (b & 1) {
      rem += a;
      if (rem >= c) {
        rem -= c;
        res++;
      }
    }
    b /= 2;
    a *= 2;
    if (a >= c) {
      a -= c;
      res += b;
    }
  }
  return res;
}
Ha.
  • 3,454
  • 21
  • 24
0

Either 128-bits and you might use Karatsuba multiplication there; or you might use the Chinese Remainder Theorem to represent (n * r.p) mod p1 and also mod p2. The second might be slower.

lorro
  • 10,687
  • 23
  • 36