интересный вопрос. Что вы подразумеваете под не в зависимости от размера
из int или количество бит в байте? Чтобы встретить другой номер
битов в байте, вам придется использовать другую машину, с
другой набор машинных инструкций, которые могут влиять или не влиять на
ответить.
Во всяком случае, смутно основываясь на первом решении, предложенном Миграном,
Я получаю:
int
topBit( unsigned x )
{
int r = 1;
if ( x > 1 ) {
if ( frexp( static_cast<double>( x ), &r ) != 0.5 ) {
++ r;
}
}
return r - 1;
}
Это работает в рамках ограничения, что входное значение должно быть точно
представимый в double
; если ввод unsigned long long
, это
может быть не так, и на некоторых более экзотических платформах это
может даже не иметь места для unsigned
.
Единственное другое постоянное время (относительно количества бит), которое я могу
думаю это:
int
topBit( unsigned x )
{
return x == 0 ? 0.0 : ceil( log2( static_cast<double>( x ) ) );
}
, который имеет то же ограничение в отношении того, что x
точно
представляются в double
, а также могут страдать от ошибок округления
присущ операциям с плавающей запятой (хотя, если log2
реализовано правильно, я не думаю, что это должно быть так). Если
ваш компилятор не поддерживает log2
(функция C ++ 11, но также присутствует
в C90, поэтому я ожидаю, что большинство компиляторов уже реализовали
это), тогда, конечно, log( x ) / log( 2 )
можно использовать, но я подозреваю,
что это увеличит риск ошибки округления, приводящей к
неправильный результат.
FWIW, я нахожу O (1) на количество битов немного нелогичным, для
причины, которые я указал выше: количество битов является лишь одним из многих
«постоянные факторы», которые зависят от машины, на которой вы работаете.
Во всяком случае, я пришел к следующему чисто целочисленному решению, которое
O (lg 1) для количества бит и O (1) для всего остального:
template< int k >
struct TopBitImpl
{
static int const k2 = k / 2;
static unsigned const m = ~0U << k2;
int operator()( unsigned x ) const
{
unsigned r = ((x & m) != 0) ? k2 : 0;
return r + TopBitImpl<k2>()(r == 0 ? x : x >> k2);
}
};
template<>
struct TopBitImpl<1>
{
int operator()( unsigned x ) const
{
return 0;
}
};
int
topBit( unsigned x )
{
return TopBitImpl<std::numeric_limits<unsigned>::digits>()(x)
+ (((x & (x - 1)) != 0) ? 1 : 0);
}
Хороший компилятор должен иметь возможность встроить рекурсивные вызовы, в результате
в близком к оптимальному коду.