Reputation: 3099
You are a C programmer given an integer i > 1
. Can you write a function msb2
that returns the second most significant bit of it, in other words:
given a number, what's the bit to the right of the leftmost 1 bit?
I.e in 1011111
it is 0, in 1100000
it is 1.
Not allowed:
Allowed are:
Is there a constant-time (constant in the number of bits, assuming that all C integer operators take constant time, and assuming that the number of bits is not constant) function for msb2
?
One idea I head: Given i
, we know i & (i >> 1) >= (1<<((int)log2(i)-1)) <=> msb2(i)=1
, however, log2(i)
can not be computed easily. Maybe this can still be used somehow?
Upvotes: 1
Views: 3616
Reputation: 5040
You can simply use
x > (x ^ (x >> 1))
The value (x ^ (x >> 1))
will start with 11
if x
starts with 10
, and with 10
if x
starts with 11
. Thus, the comparison gives exactly what is needed.
Upvotes: 7
Reputation: 72619
If you can get at the leftmost 1 bit (see msb64()
), the rest is easy:
#include <stdio.h>
#include <inttypes.h>
uint64_t msb64(uint64_t x);
int main(int argc, char **argv)
{
uint64_t x, msb;
if (argv[1] && sscanf (argv[1], "%lu", &x) == 1) {
msb = msb64(x);
printf ("msb2(%lu) = %d\n", x, x & (msb >> 1) ? 1 : 0);
}
return 0;
}
uint64_t msb64(uint64_t x)
{
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
x |= (x >> 32);
return x & ~(x >> 1);
}
$ ./a.out 11
msb2(11) = 0
$ ./a.out 15
msb2(15) = 1
Upvotes: 1
Reputation: 12515
clz
makes this trivial if it exists. If not, you can compute it constant time as follows. From this thread:
http://community.arm.com/thread/6400
static __INLINE uint32_t __CLZ(uint32_t x) {
extern uint8_t const log2Lkup[256];
if (x >= 0x00010000U) {
if (x >= 0x01000000U) {
return 8U - log2Lkup[x >> 24];
}
else {
return 16U - log2Lkup[x >> 16];
}
}
else {
if (x >= 0x00000100U) {
return 24U - log2Lkup[x >> 8];
}
else {
return 32U - log2Lkup[x];
}
}
}
The function would need the log2 (binary logarithm) lookup table defined in a .c file:
uint8_t const log2Lkup[256] = {
0U, 1U, 2U, 2U, 3U, 3U, 3U, 3U, 4U, 4U, 4U, 4U, 4U, 4U, 4U, 4U,
5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U,
6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U,
6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U,
7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U,
7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U,
7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U,
7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U,
8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,
8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,
8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,
8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,
8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,
8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,
8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,
8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U
};
I left the code intact from the website as an example as modifying the table could make this even easier.
To solve your case, take (32 - clz(x)) - 1
and handle the case where x
is 1 or 0 specially as it is undefined. Note: for 64-bit values, just add a few more lookups...
int wantedValue(int x)
{
if(x <= 1) return -1;
return (int)(32 - __CLZ(x)) - 1;
}
Upvotes: 0
Reputation: 44230
Naive log(nbits) thing: (without loops!)
#include <stdio.h>
int main(int argc, char **argv)
{
unsigned long org , val;
sscanf(argv[1] , "%lx", &org);
val = org ;
if (val >= (2<<16)) val >>= 16;
if (val >= (2<<8)) val >>= 8;
if (val >= (2<<4)) val >>= 4;
if (val >= (2<<2)) val >>= 2;
if (val >= (2<<1)) val >>= 1;
if (val <= 2) val <<= 1; // This is needed for org = 1 ;-)
printf("%lx -> %lx %c\n"
, org, val, val&1 ? '+' : '-');
return 0;
}
Upvotes: 0
Reputation: 49803
I believe this technically meets the criteria: Write 63 if
statements (assuming your integers are 64 bits in size), one for each possible solution. Yes, that basically mimics what a loop would do, but it is still constant time (which the corresponding loop would be, actually, but a tiny bit harder to argue).
Upvotes: 0