Assuming two's complement representation of signed integers and arithmetic shift behaviour of >>
operator, the answer could be:
int dl18(int x, int n) {
if (x < 0) {
x += (1 << n) - 1;
}
return x >> n;
}
The addition is necessary, because >>
rounds for negative numbers towards negative infinity. By adding 2^n - 1
, the result is always truncated towards zero, just like it happens for /
operator.
Due to your requirements, assuming that int
has 4
bytes (and to be extra pedantic CHAR_BIT = 8
), the expression may be rewritten (obfuscated) as:
(x + ((x >> 31) & ((1 << n) + ~0))) >> n
The idea of x >> 31
is to replicate MSB bit, so the mask becomes either all ones (i.e. 0xFFFFFFFF
), or all zeros, which is then used to either preserve or eliminate ((1 << n) - 1)
from addition. Parentheses around &
are necessary, because addition has higher precedence than bitwise AND.
This algorithm is also used by GCC compiler. For instance:
int dl18_4(int x) { return x / 4; }
translates with -O1
into:
dl18_4:
lea eax, [rdi+3] ; eax = rdi + 3
test edi, edi ; set sign flag if edi < 0
cmovns eax, edi ; eax = edi if SF = 0
sar eax, 2 ; eax = eax >> 2
ret
Note that shifting by negative number invokes undefined behavior, so it may be safer to declare second parameter as unsigned int
.