tags:

views:

262

answers:

7

I try to determine the right most nth bit set

if (value & (1 << 0)) { return 0; }
if (value & (1 << 1)) { return 1; }
if (value & (1 << 2)) { return 2; }
...
if (value & (1 << 63)) { return 63; }

if comparison needs to be done 64 times. Is there any faster way?

+4  A: 

There's a little trick for this:

value & -value

This uses the twos' complement integer representation of negative numbers.

Edit: This doesn't quite give the exact result as given in the question. The rest can be done with a small lookup table.

Mark Byers
Yan Cheng CHEOK
@Yan: Yes it can be lookup table of 64 entries. You might also want to check out the ctz suggestion by KennyTM. I would guess that this is faster.
Mark Byers
How do you look it up exactly? The values are powers of 2.
Ari
@Ari: Predefined hash map.
Mark Byers
Ah, Hacker's Delight!!
ardsrk
Predefined hash map? What is your hash function exactly, and is it more efficient than 64 right shifts? Does it require O(1) memory, or does it use table lookup itself? Now, of course you can (for example) use three shifts/masks/tests to isolate the byte containing the 1 and then use a 256 entry table (or use a different time-space tradeoff), but I wouldn't call that a lookup table with 64 entries. (And, of course, if you go down that path, you can solve the original problem directly. No need to turn off the non most-significant bits first.)
Ari
@ari: The hash function can be the modulo operation with a small integer. I would think that it's likely to be faster than 64 right shifts, but you should measure it to be sure.
Mark Byers
+4  A: 

If you're using GCC, use the __builtin_ctz or __builtin_ffs function. (http://gcc.gnu.org/onlinedocs/gcc-4.4.0/gcc/Other-Builtins.html#index-g_t_005f_005fbuiltin_005fffs-2894)

If you're using MSVC, use the _BitScanForward function. See http://stackoverflow.com/questions/355967/how-to-use-msvc-intrinsics-to-get-the-equivalent-of-this-gcc-code.

In POSIX there's also a ffs function. (http://linux.die.net/man/3/ffs)

KennyTM
+1: These look like very good suggestions as long as its OK to be compiler specific.
Mark Byers
A: 

Works for Visual C++ 6

int toErrorCodeBit(__int64 value) {
    const int low_double_word = value;
    int result = 0;

    __asm
    {
        bsf eax, low_double_word
        jz low_double_value_0
        mov result, eax
    }
    return result;

low_double_value_0:    
    const int upper_double_word = value >> 32;

    __asm
    {
        bsf eax, upper_double_word
        mov result, eax
    }
    result += 32;
    return result;
}
Yan Cheng CHEOK
+1  A: 

KennyTM's suggestions are good if your compiler supports them. Otherwise, you can speed it up using a binary search, something like:

int result = 0;
if (!(value & 0xffffffff)) {
    result += 32;
    value >>= 32;
}

if (!(value & 0xffff)) {
    result += 16;
    value >>= 16;
}

and so on. This will do 6 comparisons (in general, log(N) comparisons, versus N for a linear search).

Mike Seymour
+2  A: 

You could use a loop:

unsigned int value;
unsigned int temp_value;
const unsigned int BITS_IN_INT = sizeof(int) / CHAR_BIT;
unsigned int index = 0;

// Make a copy of the value, to alter.
temp_value = value;
for (index = 0; index < BITS_IN_INT; ++index)
{
    if (temp_value & 1)
    {
        break;
    }
    temp_value >>= 1;
}
return index;

This takes up less code space than the if statement proposal, with similar functionality.

Thomas Matthews
A: 

Here's another method that takes advantage of short-circuit with logical AND operations and conditional instruction execution or the instruction pipeline.

unsigned int value;

unsigned int temp_value = value;
bool bit_found = false;
unsigned int index = 0;

bit_found = !bit_found && ((temp_value & (1 << index++)); // bit 0
bit_found = !bit_found && ((temp_value & (1 << index++)); // bit 1
bit_found = !bit_found && ((temp_value & (1 << index++)); // bit 2
bit_found = !bit_found && ((temp_value & (1 << index++)); // bit 3
//...
bit_found = !bit_found && ((temp_value & (1 << index++)); // bit 64
return index - 1; // The -1 may not be necessary depending on the starting bit number.

The advantage to this method is that there are no branches and the instruction pipeline is not disturbed. This is very fast on processors that perform conditional execution of instructions.

Thomas Matthews
RaphaelSP
+1  A: 
phkahler
Note that this isn't completely portable - the first line assumes a 2's complement representation of negative integers.
Mike Seymour
@Mike. IIRC Technically you are correct. But are you aware of any C compilers that don't do that? If there are, then use -n = (~n)+1.
phkahler
It depends on the target processor, not the compiler. 2's complement is almost universal at the moment, but some Unisys mainframes apparently use 1's complement, and there's no way to know what future processors might do.
Mike Seymour
But this is certainly a good solution, especially on platforms where branches might be expensive.
Mike Seymour