views:

2433

answers:

7

This is pretty basic, but I figure it should be on SO for posterity.

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.

Thanks Stack Overflow!

+1  A: 

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

Tim Stewart
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!!
Jweede
+4  A: 

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
Um, isn't that A/B = 10**(log(A)-log(B))?
jmucchiello
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
[ +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
jmucchiello: Um, yes. Though base 10 was not meant to be implied... I will correct.
RBarryYoung
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
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
+4  A: 

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
+3  A: 

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
I agree. This is called Division by repeated subtraction.
Jweede
+3  A: 

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
what might this mysterious 'barf' operation be?
Jweede
A custom operation, of course. Do your instructions say what to do on a 0 divisor?
ysth
Thanks! I changed this code over to Python and it seems to work.
Jweede
A: 

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
}
Jweede
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
I certainly don't want to loop a gazillion times. :-(
Jweede
A: 

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