Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Left shift of only part of a number

I need to find the fastest equivalence of the following C code.

int d = 1 << x; /* d=pow(2,x) */
int j = 2*d*(i / d) + (i % d);

What I thought is to shift left upper 32 - x bits of i.
For example the following i with x=5:
1010 1010 1010 1010
will become:
0101 0101 0100 1010
Is there an assembly command for that? How can I perform this operation fast?

like image 211
Artium Avatar asked Nov 30 '22 05:11

Artium


2 Answers

divisions are slow:

int m = (1 << x) - 1;
int j = (i << 1) - (i & m);

update:

or probably faster:

int j = i + (i & (~0 << x));
like image 154
salva Avatar answered Dec 05 '22 07:12

salva


x86 32bit assembly (AT&T syntax):

/* int MaskedShiftByOne(int val, int lowest_bit_to_shift) */
mov 8(%esp), %ecx
mov $1, %eax
shl %ecx, %eax            ; does 1 << lowest_bit_to_shift
mov 4(%esp), %ecx
dec %eax                  ; (1 << ...) - 1 == 0xf..f (lower bitmask)
mov %eax, %edx
not %edx                  ; complement - higher mask
and %ecx, %edx            ; higher bits
and %ecx, %eax            ; lower bits
lea (%eax, %edx, 2), %eax ; low + 2 * high
ret

This should work both on Linux and Windows.

Edit: the i + (i & (~0 << x)) is shorter:

mov 4(%esp), %ecx
mov $-1, %eax
mov 8(%esp), %edx
shl %edx, %eax
and %ecx, %eax
add %ecx, %eax
ret

Morale: Don't ever start with assembly. If you really need it, disassemble highly-optimized compiler output ...

like image 43
FrankH. Avatar answered Dec 05 '22 05:12

FrankH.