Theory
Suppose we have 2 unsigned integer A and B. B is a power of 2 ie there is a number b such as B = 1U << b
It can be checked with the following code
static inline ispowerof2(int b) { return b & !(b & (b - 1)); }
b can be computed by ilog2 function
int ilog2(unsigned int v) { int i = -1; while(v) { i++; v >>= 1; } return v; }
or from http://en.wikipedia.org/wiki/Binary logarithm#Integer
/ * Returns the floor form of binary logarithm for a 32 bit integer. * −1 is returned if ''n'' is 0. */ int floorLog2(unsigned int n) { if (n == 0) return -1;int pos = 0; if (n >= 1<<16) { n >>= 16; pos += 16; } if (n >= 1<< 8) { n >>= 8; pos += 8; } if (n >= 1<< 4) { n >>= 4; pos += 4; } if (n >= 1<< 2) { n >>= 2; pos += 2; } if (n >= 1<< 1) { pos += 1; } return pos; }
In this case we have :
- A * B == A << b
- A / B == A >> b
- A % B == A & (B - 1) == A & ((1U << b) - 1)
Usage in C
Either we can define your own function for * / % or we can use compiler optimisation. If we replace B by 1 << b (with a macro), then
int divu(uint a, uint b) { return a / (1U << b); }int modu(uint a, uint b) { return a % (1U << b); }
int mulu(uint a, uint b) { return a * (1U << b); }
will give you in arm assembly (arm-linux-gnueabi-gcc -Os -S )
divu: mov r0, r0, lsr r1 mov pc, lr modu: mvn r3, #0 bic r0, r0, r3, asl r1 mov pc, lr mulu: mov r0, r0, asl r1 mov pc, lr
That's pretty fast without ugly operator redefinition !!!
But add and sub are slower [1], because we need to compute the shift.
Note a gcc missoptimisation (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48812)
int divu3(uint a, uint b) { return a / ((1U<<b) / 4); }
divu3: stmfd sp!, {r3, lr} mov r3, #1 mov r1, r3, asl r1 mov r1, r1, lsr #2 bl {aeabi_uidiv ldmfd sp!, {r3, pc}
This is unfortunate, the compiler could have generated :
divu3: mov r3, #1 mov r1, r3, asl r1 mov r1, r1, lsr #2 mov r0, r0, lsr r1
[1]
int addu(uint a, uint b) { return a + (1U << b); }
addu: mov r3, #1 add r0, r0, r3, asl r1 mov pc, lr