Reputation: 15806
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.
Upvotes: 17
Views: 14346
Reputation: 1
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.
Upvotes: 0
Reputation: 15806
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
}
Upvotes: 0
Reputation: 98398
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.
Upvotes: 11
Reputation: 44266
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.
Upvotes: 0
Reputation: 56735
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:
A/B = 2^(log2(A)-log2(b)): If you can get the logarithm of the values, you can closely approximate the division.
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)
Upvotes: 4
Reputation: 45117
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.
Upvotes: 3
Reputation: 5242
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
Upvotes: 4
Reputation: 5500
Jweede, I had no idea how to solve your problem but I found a seemingly relevent post here.
Upvotes: 1