15

I need to implement a simple macro that finds the modulo of two numbers on a processor that doesn't have a division operator (think ARM). I could use division by repeated subtraction, but I don't know if this was the most efficient or easiest to work with.

Any suggestions? Code would be even more helpful. This particular class has us using a subset of SPARC, so most operations look like this: add r1, r2, rdest.

This particular assignment calls for checking that a mod b == 0 or that the remainder of the division is zero. So any hints or suggestions toward an efficient implementation would be most welcome.

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
Jon W
  • 15,480
  • 6
  • 37
  • 47

8 Answers8

10

No idea what exact operations you are limited to, but I'd think you'd do long division, something like this, in pseudo-code:

dividend = abs(dividend)
divisor = abs(divisor)
if divisor == 0,
    barf
remainder = dividend
next_multiple = divisor

do
    multiple = next_multiple
    next_multiple = left_shift(multiple, 1)
while next_multiple <= remainder && next_multiple > multiple

while multiple >= divisor,
    if multiple <= remainder,
        remainder = remainder - multiple
    multiple = right_shift(multiple, 1)

To actually calculate the quotient (or at least its absolute value), the last part would be something like:

quotient = 0
while multiple >= divisor,
    quotient = left_shift(quotient, 1);
    if multiple <= remainder,
        remainder = remainder - multiple
        quotient = quotient + 1
    multiple = right_shift(multiple, 1)

None of this is tested, and it is probably riddled with errors.

ysth
  • 96,171
  • 6
  • 121
  • 214
4

I can think of two possible approaches. Because this is homework I will just mention them and let you work if they are feasible and how to implement them:

  1. A/B = 2^(log2(A)-log2(b)): If you can get the logarithm of the values, you can closely approximate the division.

  2. Binary Long Division: You learned how to do decimal long division before you could do division, right? So teach your computer to do binary long division (it should actually be easier in binary).

(edit: corrected #1., the log division equation)

RBarryYoung
  • 55,398
  • 14
  • 96
  • 137
  • Um, isn't that A/B = 10**(log(A)-log(B))? – jmucchiello Jun 02 '09 at 06:51
  • You've suggested approaches to get the quotient, what the OP is asking for is the remainder. Besides, even halfway decent approximation of division using logs requires floating point precision, which is overkill to find integer remainder. @jmucchiello: You're right, but the base is more likely to be 2 rather than 10, considering the situation. – sykora Jun 02 '09 at 07:32
  • [ +1 for tagging homework yourself ] Definitely review yourself how to do multi-digit division in paper-and-pencil (oh shock dead trees!) and then implement the same in your program. ps. Bonus points if you also do the same for square roots ;) – winden Jun 02 '09 at 07:39
  • jmucchiello: Um, yes. Though base 10 was not meant to be implied... I will correct. – RBarryYoung Jun 02 '09 at 07:53
  • sykora: Yes, this is homework, so I did not give the entire answer (and neither should anyone else). I think that most of us know how to go from the quotient to the remainder here and that it is pretty easy. Re, Log approximation: binary logs close enough for approxmation can be extracted from integers without FP (via Left Shifts). I know because I did it 35 years ago with half-adder circuits. You will note that someone else here has already figured this out as well. – RBarryYoung Jun 02 '09 at 08:02
  • winden: IIRC, there is a very nice convergence for SQRT that does not require division (other than by 2, which is easily emulated). So I think that it's actually easier than division. – RBarryYoung Jun 02 '09 at 08:05
3

This doesn't answer your question directly but is an interesting case nonetheless. If the number is being modulo'd by a power of two the operation can be performed as

x % 2^n = x & (2^n - 1)

Which uses a single AND operation, which usually is a one or two cycle operation.

More information At Wikipedia

mdec
  • 5,122
  • 4
  • 25
  • 26
3

Seems like subtracting (or adding if a is negative) by b until you hit or cross 0 would be an easy implementation albeit almost certainly not the most efficient.

JP Alioto
  • 44,864
  • 6
  • 88
  • 112
1

Jweede, I had no idea how to solve your problem but I found a seemingly relevent post here.

Tim Stewart
  • 5,350
  • 2
  • 30
  • 45
  • that's a nice summary of optimizations for the mod op. I'll definitely tuck this site away if I have to write a compiler for class. Thanks!! – Jon W Jun 02 '09 at 06:10
0

mod can be computed bit by bit:

int r = 0;
int q = 0;
for (int i = sizeof(n) * 8 - 1; i >= 0; --i) {
  r <<= 1;
  r |= (n >> i) & 1;
  if (r > d) {
    r -= d;
    q |= 1 << i;
  }
}
return r;

That gives you the remainder, q would be the quotient. If you have bsrl instruction, you can set a better high bound for i, since you can start at the most significant bit only.

0

Thanks for the advice all!

I started using a simple division by repeated subtraction algorithm to implement this. But as pointed out by ysth, there's a much easier way. Here's the first algorithm:

        .macro mod a, b, r
        mov a, r
divlp:  sub r, b, r
        cmp r, b
        bge divlp
        .endmacro

This closely resembles:

mod(a, b){
   int r = a
   while(r >= b){
      r = r - b
   }
   return r
}
Jon W
  • 15,480
  • 6
  • 37
  • 47
  • Yes, there's a more efficient way; see my answer. It may look like a lot more code, but each loop only executes at most 32 or 64 times, as opposed to your loop which may execute a bazillion times. – ysth Jun 03 '09 at 14:23
  • I certainly don't want to loop a gazillion times. :-( – Jon W Jun 03 '09 at 19:28
0

A/B=Q, therefore A=B*Q. We know both A & B, we want Q.

My idea to add to the mix: Binary search Q. Start with Q=0 & Q=1, perhaps as base cases. Keep doubling until B * Q > A, and then you've got two bounds (Q and Q/2), so find the correct Q between the two of those. O(log(A/B)), but a bit trickier to implement:

#include <stdio.h>
#include <limits.h>
#include <time.h>

// Signs were too much work.
// A helper for signs is easy from this func, too.
unsigned int div(unsigned int n, unsigned int d)
{
    unsigned int q_top, q_bottom, q_mid;
    if(d == 0)
    {
        // Ouch
        return 0;
    }

    q_top = 1;
    while(q_top * d < n && q_top < (1 << ((sizeof(unsigned int) << 3) - 1)))
    {
        q_top <<= 1;
    }
    if(q_top * d < n)
    {
        q_bottom = q_top;
        q_top = INT_MAX;
    }
    else if(q_top * d == n)
    {
        // Lucky.
        return q_top;
    }
    else
    {
        q_bottom = q_top >> 1;
    }

    while(q_top != q_bottom)
    {
        q_mid = q_bottom + ((q_top - q_bottom) >> 1);
        if(q_mid == q_bottom)
            break;

        if(d * q_mid == n)
            return q_mid;
        if(d * q_mid > n)
            q_top = q_mid;
        else
            q_bottom = q_mid;
    }
    return q_bottom;
}

int single_test(int n, int d)
{
    int a = div(n, d);
    printf("Single test: %u / %u = %u\n", n, d, n / d);
    printf(" --> %u\n", a);
    printf(" --> %s\n", a == n / d ? "PASSED" : "\x1b[1;31mFAILED\x1b[0m");
}

int main()
{
    unsigned int checked = 0;
    unsigned int n, d, a;

    single_test(1389797028, 347449257);
    single_test(887858028, 443929014);
    single_test(15, 5);
    single_test(16, 4);
    single_test(17, 4);
    single_test(0xFFFFFFFF, 1);

    srand(time(NULL));

    while(1)
    {
        n = rand();
        d = rand();

        if(d == 0)
            continue;

        a = div(n, d);
        if(n / d == a)
            ++checked;
        else
        {
            printf("\n");
            printf("DIVISION FAILED.\n");
            printf("%u / %u = %u, but we got %u.\n", n, d, n / d, a);
        }

        if((checked & 0xFFFF) == 0)
        {
            printf("\r\x1b[2K%u checked.", checked);
            fflush(stdout);
        }
    }

    return 0;
}

Additionally, you can also iterate through the bits, setting each one to 1. If B * Q <= A is true, keep the bit as 1, otherwise zero it. Proceed MSB->LSB. (You will need to be able to detect it B*Q will overflow, however.

Thanatos
  • 42,585
  • 14
  • 91
  • 146