Узнайте, как часто х можно разделить на 2 без цикла в C
Я ищу способ узнать, как часто я могу разделить константу x на два (и не получить остаток) без использования циклов, рекурсии или логарифма. Поскольку это та же проблема, что и при поиске индекса наименьшего значимого ненулевого бита, я надеюсь, что для этого есть способ использовать побитовые операции. К сожалению, я не смог придумать это. Есть идеи?
Справочная информация: у меня есть цикл, который удваивает счетчик на каждой итерации, пока он больше не делит константу х. Я хочу, чтобы этот цикл был развернут, но компилятор NVIDIA CUDA недостаточно умен, чтобы вычислить количество итераций, поэтому я хочу переписать цикл таким образом, чтобы число итераций стало более очевидным для компилятора:
for(i=1; CONST & i == 0; i *= 2)
bla(i);
должно стать чем-то вроде
#define ITERATIONS missing_expr_with_CONST
for(i=0; i < ITERATIONS; i++)
fasel(i);
4 ответа
Это можно решить напрямую, используя этот код для 32-разрядных чисел (я не беру кредит).
unsigned int v; // find the number of trailing zeros in 32-bit v
int r; // result goes here
static const int MultiplyDeBruijnBitPosition[32] =
{
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
r = MultiplyDeBruijnBitPosition[((uint32_t)((v & -v) * 0x077CB531U)) >> 27];
Вы также можете сделать это с помощью чистой арифметики без справочной таблицы (подход последовательности Де Брюйна требует справочную таблицу), но это дорого. Здесь это идет:
m = (v&-v)-1;
i = ((m)/((m)%255+1) / 255%255*8 + 7-86/((m)%255+12));
i
это индекс самого низкого установленного бита в v
, Формула действительна для значений v
до 2000 или около того нулевых битов, намного больше, чем любой целочисленный тип.
Смотрите здесь для объяснения:
Есть ли способ вычислить ширину целочисленного типа во время компиляции?
Это может быть легко сделано с помощью цикла (это может быть не на 100% правильно, поэтому не стесняйтесь исправлять):
int leastSigBit(int num) {
int result;
for(result = 0; num & 1 != 1; ++result)
num >>= 1;
return result;
}
Но я считаю, что это невозможно сделать без цикла, поскольку вы ищете бит в неизвестной позиции и, следовательно, должны проверить все возможные позиции.
РЕДАКТИРОВАТЬ Пересмотрено на основе обновленного описания.
Если "pow" не считается логарифмом, вот способ вычисления всех чисел, а не только "2". Обратные функции, которые умножают вместо деления, также включены для полноты.
Код GLSL:
//://////////////////////////////////////////////////////////://
//://///://
//:IFDO: Move this somewhere more general IF you //://///://
//: ever find a need to call it from somewhere //://///://
//: else. //://///://
int //://///://
ES2_AA2_Di2( //://///://
int B //:BASE:(Affected_Number) //://///://
, int N //:NUMB:(Number_Of_Times_To_Divide_By_Two) //://///://
){ //://///://
//:programtic_equivalent_of_formula_below:---------------://
//: ://
//: var b = B; ://
//: for( var n = 0; n < N; n++ ){ ://
//: b = b / 2; ://
//: };; ://
//: return( b /** R **/ ); ://
//:______________________________________________________://
int R = int( float( 0 )
+ float( B )
/ pow( float(2) , float(N) )
);;
return( R );
return( int(0) );
}//://////////////////////////////////////////| ES2_AA2_Di2 |://
//://////////////////////////////////////////////////////////://
Код JavaScript:
//:B: Base //: MUL:A_NUMBER_OF_TIMES ://
//:N: N number of times. //: MUL:A_NUMBER_OF_TIMES ://
const AA2_Mu2=( B, N )=>{ return( B * (pow(2,N) ) ); };
const AA2_Mu3=( B, N )=>{ return( B * (pow(3,N) ) ); };
const AA2_Mu4=( B, N )=>{ return( B * (pow(4,N) ) ); };
const AA2_Mu5=( B, N )=>{ return( B * (pow(5,N) ) ); };
//:B: Base //: DIV:A_NUMBER_OF_TIMES ://
//:N: N number of times. //: DIV:A_NUMBER_OF_TIMES ://
const AA2_Di2=( B, N )=>{ return( B / (pow(2,N) ) ); };
const AA2_Di3=( B, N )=>{ return( B / (pow(3,N) ) ); };
const AA2_Di4=( B, N )=>{ return( B / (pow(4,N) ) ); };
const AA2_Di5=( B, N )=>{ return( B / (pow(5,N) ) ); };
Расшифровка текста на C оставлена читателю в качестве упражнения.
Сделайте правое смещение, затем левое смещение и проверьте равенство с AND, увеличивайте счетчик для каждого успешного выполнения этой проверки.
int powerOfTwo(int x) {
int counter = 0;
while(x > 0) {
int y = x >> 1;
y <<= 1;
if(x ^ y) {
counter++;
} else {
break;
}
x >>= 1;
}
return counter;
}
Это использует цикл, хотя... Я могу думать о способах устранения цикла, но это в основном "развертывание" цикла.