Quero escrever uma função que retorne a próxima potência mais próxima de 2 números. Por exemplo, se minha entrada for 789, a saída deve ser 1024. Existe alguma maneira de conseguir isso sem usar loops, mas apenas usando alguns operadores bit a bit?
c
optimization
bit-manipulation
Naveen
fonte
fonte
Respostas:
Verifique os Bit Twiddling Hacks . Você precisa obter o logaritmo da base 2 e adicionar 1 a isso. Exemplo para um valor de 32 bits:
A extensão para outras larguras deve ser óbvia.
fonte
uint64_t next_pow2(uint64_t x) { return x == 1 ? 1 : 1<<(64-__builtin_clzl(x-1)); }
E por 32 bits:uint32_t next_pow2(uint32_t x) { return x == 1 ? 1 : 1<<(32-__builtin_clz(x-1)); }
isto é, se você usar o GCC (e Clang, eu acho?), Mas seria aconselhável reservar um tempo para encontre a chamada para o CLZ em vez de copiar e colar todas as opções existentes.x > UINT32_MAX
não é sem ramo. Além disso, o GCC e o Clang usam-mtune=generic
por padrão (como a maioria das distros), portanto seu código NÃO será expandido para aslzcnt
instruções em x86_64 - ele será expandido para algo MUITO mais lento (uma rotina libgcc), a menos que você use algo parecido-march=native
. Portanto, a substituição proposta é não portátil, com bugs e (normalmente) mais lenta.Isso funciona ao encontrar o número pelo qual você aumentaria 2 para obter x (pegue o log do número e divida pelo log da base desejada, consulte a Wikipedia para mais informações ). Em seguida, arredonde isso para cima para obter a potência numérica inteira mais próxima.
Este é um método de propósito mais geral (ou seja, mais lento!) Do que os métodos bit a bit vinculados em outros lugares, mas é bom saber matemática, não é?
fonte
log(pow(2,29))/log(2)
= 29.000000000000004, então o resultado é 2 30 em vez de retornar 2 29. Acho que é por isso que as funções log2 existem?fonte
uint32_t
.Eu acho que isso funciona também:
E a resposta é
power
.fonte
power <<= 1
x
for muito grande (ou seja, bits insuficientes para representar a próxima potência de 2).Se você estiver usando o GCC, consulte Otimizando a função next_pow2 () da Lockless Inc. Esta página descreve uma maneira de usar a função interna
builtin_clz()
(conte o zero à esquerda) e, posteriormente, use diretamente x86 (ia32) instrução assemblerbsr
(inversa bit scan), tal como é descrito em outra resposta 's link para o site gamedev . Esse código pode ser mais rápido do que os descritos na resposta anterior .A propósito, se você não usar as instruções do assembler e o tipo de dados de 64 bits, poderá usar este
fonte
_BitScanForward
no Visual C ++__builtin_ctz()
__builtin_ctz()
não será útil para arredondar qualquer poder não de 2 número até a próxima potência de doisconstexpr uint64_t nextPowerOfTwo64 (uint64_t x) { return 1ULL<<(sizeof(uint64_t) * 8 - __builtin_clzll(x)); }
Mais uma, embora eu use ciclo, mas isso é muito mais rápido que os operandos matemáticos
potência de duas opções de "andar":
potência da opção de dois "ceil":
ATUALIZAR
Como mencionado nos comentários, houve um erro no local
ceil
onde o resultado foi errado.Aqui estão as funções completas:
fonte
x
potência é 2. É necessário um micro para testar se a entrada é a potência 2.#define ISPOW2(x) ((x) > 0 && !((x) & (x-1)))
if (x == 0) return 1; /* Or 0 (Which is what I use) */ x--; /* Rest of program */
power of two "ceil" option
não está correto. Por exemplo, quandox = 2
o resultado deve ser em2
vez de4
Para qualquer tipo não assinado, com base nos Bit Twiddling Hacks:
Não há realmente um loop lá, pois o compilador sabe em tempo de compilação o número de iterações.
fonte
std::is_unsigned<UnsignedType>::value
afirmação.Para carros alegóricos IEEE, você seria capaz de fazer algo assim.
Se você precisar de uma solução inteira e conseguir usar a montagem em linha, o BSR fornecerá o log2 de um número inteiro no x86. Ele conta quantos bits corretos estão configurados, o que é exatamente igual ao log2 desse número. Outros processadores têm instruções semelhantes (geralmente), como CLZ e, dependendo do seu compilador, pode haver um intrínseco disponível para fazer o trabalho por você.
fonte
Apesar da pergunta está marcado como
c
aqui meus cinco centavos. Para nossa sorte, o C ++ 20 incluiriastd::ceil2
estd::floor2
(veja aqui ). Sãoconsexpr
funções de modelo, a implementação atual do GCC usa deslocamento de bits e funciona com qualquer tipo integral não assinado.fonte
bit_ceil
open-std.org/JTC1/SC22/WG21/docs/papers/2020/p1956r1.pdfSe você não deseja se aventurar no domínio do comportamento indefinido, o valor da entrada deve estar entre 1 e 2 ^ 63. A macro também é útil para definir constante no tempo de compilação.
fonte
Para completar, aqui está uma implementação de ponto flutuante no padrão pântano C.
fonte
rep bsr ecx,eax; mov eax,0; cmovnz eax,2; shl eax,cl
é cerca de 25x mais rápido.Uma solução específica eficiente da Microsoft (por exemplo, Visual Studio 2017) em C / C ++ para entrada inteira. Manipula o caso da entrada que corresponde exatamente a uma potência de dois valores, decrementando antes de verificar a localização do 1 bit mais significativo.
Isso gera 5 ou mais instruções embutidas para um processador Intel semelhante ao seguinte:
Aparentemente, o compilador do Visual Studio C ++ não é codificado para otimizar isso para valores em tempo de compilação, mas não é como se houvesse muitas instruções lá.
Editar:
Se você deseja que um valor de entrada 1 dê 1 (2 à potência zero), uma pequena modificação no código acima ainda gera instruções diretas, sem ramificação.
Gera apenas mais algumas instruções. O truque é que o Index pode ser substituído por um teste seguido por uma instrução cmove.
fonte
No x86, você pode usar as instruções de manipulação do sse4 bit para torná-lo mais rápido.
Em c, você pode usar as intrínsecas correspondentes.
fonte
Aqui está a minha solução em C. Espero que isso ajude!
fonte
Muitas arquiteturas de processador oferecem suporte
log base 2
ou operação muito semelhante -count leading zeros
. Muitos compiladores têm intrínsecas para isso. Consulte https://en.wikipedia.org/wiki/Find_first_setfonte
Supondo que você tenha um bom compilador e ele possa fazer um pouco de distorção antes da mão, isso está acima de mim neste momento, mas de qualquer maneira isso funciona !!!
Código de teste abaixo:
Saídas:
fonte
Estou tentando obter a potência mais baixa mais baixa de 2 e fiz essa função. Pode multiplicar o número mais baixo 2 vezes mais próximo para obter a potência superior mais próxima de 2
fonte
Resposta adaptada de Paul Dixon ao Excel, isso funciona perfeitamente.
fonte
Uma variante da resposta @YannDroneaud válida para
x==1
, apenas para chapas x86, compiladores, gcc ou clang:fonte
Aqui está o que estou usando para que essa seja uma expressão constante, se a entrada for uma expressão constante.
Por exemplo, uma expressão como:
reduzirá muito bem a uma constante.
fonte
Você pode achar que o seguinte esclarecimento é útil para seu objetivo:
fonte
Converta-o em um flutuador e use .hex (), que mostra a representação IEEE normalizada.
>>> float(789).hex() '0x1.8a80000000000p+9'
Em seguida, basta extrair o expoente e adicionar 1.
>>> int(float(789).hex().split('p+')[1]) + 1 10
E aumente 2 para esse poder.
>>> 2 ** (int(float(789).hex().split('p+')[1]) + 1) 1024
fonte
fonte
Se você precisar para coisas relacionadas ao OpenGL:
fonte
Se você deseja um modelo de uma linha. Aqui está
ou
fonte
n
várias vezes sem um ponto de sequência é inválido. Você escreveu como sen-=1
deveria acontecer primeiro, mas a única garantia aqui é quen
contém seu novo valor depois que;
os parênteses e não mudam isso.