Se eu tiver algum número inteiro n e quiser saber a posição do bit mais significativo (ou seja, se o bit menos significativo estiver à direita, quero saber a posição do bit mais à esquerda que é 1), qual é o método mais rápido / eficiente de descobrir?
Eu sei que POSIX oferece suporte a um ffs()
método em strings.h para encontrar o primeiro conjunto de bits, mas não parece haver um fls()
método correspondente .
Existe alguma maneira realmente óbvia de fazer isso que estou perdendo?
E nos casos em que você não pode usar funções POSIX para portabilidade?
Edit: Que tal uma solução que funciona em arquiteturas de 32 e 64 bits (muitas das listagens de código parecem que só funcionam em ints de 32 bits).
Respostas:
GCC tem :
Eu esperaria que eles fossem traduzidos em algo razoavelmente eficiente para sua plataforma atual, seja um daqueles algoritmos sofisticados de bit-twiddling ou uma única instrução.
Um truque útil se a sua entrada pode ser zero é
__builtin_clz(x | 1)
: incondicionalmente definindo o baixo bit sem modificar quaisquer outros faz com que a saída31
parax=0
, sem alterar a saída para qualquer outra entrada.Para evitar a necessidade de fazer isso, sua outra opção são intrínsecos específicos da plataforma, como ARM GCC
__clz
(nenhum cabeçalho necessário) ou x86_lzcnt_u32
em CPUs que suportam alzcnt
instrução. (Cuidado com issolzcnt
decodifica comobsr
em CPUs mais antigas em vez de falhas, o que dá 31-lzcnt para entradas diferentes de zero.)Infelizmente, não há como aproveitar as vantagens das várias instruções CLZ em plataformas não x86 que definem o resultado para input = 0 como 32 ou 64 (de acordo com a largura do operando). O x86 também
lzcnt
faz isso, enquantobsr
produz um índice de bits que o compilador deve inverter a menos que você use31-__builtin_clz(x)
.(O "resultado indefinido" não é C Undefined Behavior, apenas um valor que não está definido. É na verdade tudo o que estava no registro de destino quando a instrução foi executada. AMD documenta isso, Intel não, mas CPUs da Intel implementam esse comportamento . Mas ele não o que estava anteriormente na variável C você está atribuindo a, isso não é geralmente como as coisas funcionam quando gcc transforma C em asm. Veja também por que quebrar a "saída de dependência" de LZCNT importa? )
fonte
__builtin_ctz
overffs
, que compila em um BSF e um CMOV para lidar com o caso de entrada era zero. Em arquiteturas sem uma implementação curta o suficiente (por exemplo, ARM antigo sem aclz
instrução), o gcc emite uma chamada para uma função auxiliar libgcc.Supondo que você esteja no x86 e jogo para um pouco de montador embutido, a Intel fornece uma
BSR
instrução ("varredura reversa de bits"). É rápido em alguns x86s (microcodificado em outros). Do manual:(Se você estiver no PowerPC, há uma
cntlz
instrução semelhante ("contar zeros à esquerda").)Código de exemplo para gcc:
Veja também este tutorial de assembler embutido , que mostra (seção 9.4) que ele é consideravelmente mais rápido do que código em loop.
fonte
Como 2 ^ N é um número inteiro com apenas o enésimo bit definido (1 << N), encontrar a posição (N) do bit mais alto é o log de número inteiro de base 2 desse inteiro.
http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious
Este algoritmo "óbvio" pode não ser transparente para todos, mas quando você percebe que o código muda um bit repetidamente para a direita até que o bit mais à esquerda seja deslocado (observe que C trata qualquer valor diferente de zero como verdadeiro) e retorna o número de turnos, faz todo o sentido. Também significa que funciona mesmo quando mais de um bit é definido - o resultado é sempre para o bit mais significativo.
Se você rolar para baixo nessa página, verá variações mais rápidas e complexas. No entanto, se você sabe que está lidando com números com muitos zeros à esquerda, a abordagem ingênua pode fornecer uma velocidade aceitável, uma vez que o deslocamento de bits é bastante rápido em C e o algoritmo simples não requer a indexação de um array.
NOTA: Ao usar valores de 64 bits, seja extremamente cauteloso ao usar algoritmos muito inteligentes; muitos deles funcionam corretamente apenas para valores de 32 bits.
fonte
>>>
. Além disso, provavelmente o comparador!= 0
e algum número não especificado de parênteses.Isso deve ser rápido:
fonte
Isso é como encontrar um tipo de log de inteiro. Existem pequenos truques, mas fiz minha própria ferramenta para isso. O objetivo, é claro, é velocidade.
Minha constatação é que a CPU já tem um detector automático de bits, usado para conversão de inteiro para float! Então use isso.
Essa versão converte o valor em um duplo e, em seguida, lê o expoente, que informa onde o bit estava. A mudança e subtração extravagantes são extrair as partes adequadas do valor IEEE.
É um pouco mais rápido usar floats, mas um float só pode fornecer as primeiras posições de 24 bits por causa de sua precisão menor.
Para fazer isso com segurança, sem comportamento indefinido em C ++ ou C, use em
memcpy
vez de conversão de ponteiro para trocadilhos. Os compiladores sabem como embuti-lo de forma eficiente.Ou em C99 e posterior, use a
union {double d; uint32_t u[2];};
. Mas note que em C ++, o tipo de união punning só é suportado em alguns compiladores como uma extensão, não em ISO C ++.Isso geralmente será mais lento do que um intrínseco específico de plataforma para uma instrução de contagem de zeros à esquerda, mas o ISO C portátil não tem essa função. Algumas CPUs também carecem de uma instrução de contagem zero à esquerda, mas algumas delas podem converter números inteiros em com eficiência
double
. A conversão de um padrão de bit FP de volta para um inteiro pode ser lenta, porém (por exemplo, no PowerPC, isso requer um armazenamento / recarregamento e geralmente causa um bloqueio de carregamento, acerto e armazenamento).Este algoritmo pode ser potencialmente útil para implementações SIMD, porque menos CPUs têm SIMD
lzcnt
. x86 só obteve tal instrução com AVX512CDfonte
Kaz Kylheku aqui
Eu comparei duas abordagens para este número de mais de 63 bits (o tipo long long no gcc x86_64), ficando longe do bit de sinal.
(Acontece que preciso deste "encontrar o bit mais alto" para algo, você vê.)
Implementei a pesquisa binária baseada em dados (estritamente baseada em uma das respostas acima). Eu também implementei uma árvore de decisão completamente desenrolada manualmente, que é apenas um código com operandos imediatos. Sem loops, sem tabelas.
A árvore de decisão (higher_bit_unrolled) foi avaliada como 69% mais rápida, exceto para o caso n = 0 para o qual a pesquisa binária tem um teste explícito.
O teste especial da busca binária para 0 caso é apenas 48% mais rápido do que a árvore de decisão, que não tem um teste especial.
Compilador, máquina: (GCC 4.5.2, -O3, x86-64, 2867 Mhz Intel Core i5).
Programa de teste rápido e sujo:
Usando apenas -O2, a diferença se torna maior. A árvore de decisão é quase quatro vezes mais rápida.
Eu também comparei com o código ingênuo de mudança de bits:
Isso é rápido apenas para números pequenos, como seria de esperar. Ao determinar que o bit mais alto é 1 para n == 1, fez o benchmarking mais de 80% mais rápido. No entanto, metade dos números escolhidos aleatoriamente no espaço de 63 bits têm o conjunto de 63 bits!
Na entrada 0x3FFFFFFFFFFFFFFF, a versão da árvore de decisão é um pouco mais rápida do que em 1 e mostra ser 1120% mais rápida (12,2 vezes) do que o bit shifter.
Também vou comparar a árvore de decisão com os builtins do GCC e também tentar uma mistura de entradas em vez de repetir com o mesmo número. Pode haver alguma previsão de branch travado acontecendo e talvez alguns cenários de cache irrealistas que o tornam artificialmente mais rápido nas repetições.
fonte
A respeito
?
fonte
1 registro, 13 instruções. Acredite ou não, isso geralmente é mais rápido do que a instrução BSR mencionada acima, que opera em tempo linear. Este é o tempo logarítmico.
De http://aggregate.org/MAGIC/#Most%20Significant%201%20Bit
fonte
__builtin_clz
se estiver habilitado com-march=native
ou algo assim (já que é rápido em todos os CPUs que o suportam). Mesmo em CPUs como a família AMD Bulldozer, onde o BSR é "lento", não é tão lento: 7 m-ops com latência de 4 ciclos e um por rendimento de 4c. No Atom, o BSR é muito lento: 16 ciclos. Em Silvermont, é 10 uops com latência de 10 ciclos. Isso pode ser uma latência um pouco menor do que BSR em Silvermont, mas IDK.Aqui estão alguns benchmarks (simples) de algoritmos fornecidos atualmente nesta página ...
Os algoritmos não foram testados em todas as entradas de int sem sinal; então verifique isso primeiro, antes de usar algo às cegas;)
Na minha máquina, clz (__builtin_clz) e asm funcionam melhor. asm parece ainda mais rápido do que clz ... mas pode ser devido ao benchmark simples ...
fonte
Embora eu provavelmente só usasse esse método se absolutamente exigisse o melhor desempenho possível (por exemplo, para escrever algum tipo de IA de jogo de tabuleiro envolvendo quadros de bits), a solução mais eficiente é usar o ASM embutido. Consulte a seção Otimizações desta postagem do blog para obter o código com uma explicação.
fonte
Eu precisava de uma rotina para fazer isso e antes de pesquisar na web (e encontrar esta página), criei minha própria solução baseada em uma pesquisa binária. Embora eu tenha certeza de que alguém já fez isso antes! Ele roda em tempo constante e pode ser mais rápido do que a solução "óbvia" postada, embora eu não esteja fazendo grandes afirmações, apenas postando por interesse.
fonte
isso é algum tipo de pesquisa binária, funciona com todos os tipos de inteiros (sem sinal!)
para completar:
fonte
typedef
s ou qualquer coisa exceto macros de pré-processador. Esta é uma convenção amplamente aceita.Algumas respostas excessivamente complexas aqui. A técnica Debruin só deve ser usada quando a entrada já é uma potência de dois, caso contrário, há uma maneira melhor. Para uma potência de 2 entradas, o Debruin é o mais rápido absoluto, ainda mais rápido do que
_BitScanReverse
em qualquer processador que testei. No entanto, no caso geral,_BitScanReverse
(ou qualquer que seja o nome do intrínseco em seu compilador) é o mais rápido (embora em certas CPUs ele possa ser microcodificado).Se a função intrínseca não for uma opção, aqui está uma solução de software ideal para processar entradas gerais.
Observe que esta versão não requer uma consulta de Debruin no final, ao contrário da maioria das outras respostas. Ele calcula a posição no lugar.
As tabelas podem ser preferíveis, no entanto, se você chamá-las repetidamente o suficiente, o risco de uma falha de cache será eclipsado pelo aumento da velocidade de uma tabela.
Isso deve produzir o maior rendimento de qualquer uma das respostas de software fornecidas aqui, mas se você apenas ligar ocasionalmente, prefira uma solução livre de tabela como meu primeiro trecho.
fonte
Como as respostas acima indicam, há várias maneiras de determinar o bit mais significativo. No entanto, como também foi apontado, os métodos provavelmente serão exclusivos para registradores de 32 ou 64 bits. A página stanford.edu bithacks fornece soluções que funcionam para computação de 32 bits e 64 bits. Com um pouco de trabalho, eles podem ser combinados para fornecer uma abordagem sólida de arquitetura cruzada para obter o MSB. A solução que cheguei para compilar / trabalhar em computadores de 64 e 32 bits foi:
fonte
#ifdef BUILD_64
bandeira? Nesse caso, não seria necessário redefinir dentro da condicional.Uma versão em C usando aproximação sucessiva:
Vantagem: o tempo de execução é constante independentemente do número fornecido, pois o número de loops é sempre o mesmo. (4 loops ao usar "unsigned int")
fonte
msb += (n>>msb) ? step : -step;
), mais compiladores provavelmente criarão asm sem ramificação, evitando erros de previsão de ramificação em cada etapa ( stackoverflow.com/questions/11227809/… ).Eu sei que esta questão é muito antiga, mas apenas tendo implementado uma função msb () eu mesmo descobri que a maioria das soluções apresentadas aqui e em outros sites não são necessariamente as mais eficientes - pelo menos para minha definição pessoal de eficiência (veja também Atualização abaixo ) Aqui está o porquê:
A maioria das soluções (especialmente aquelas que empregam algum tipo de esquema de busca binária ou a abordagem ingênua que faz uma varredura linear da direita para a esquerda) parecem negligenciar o fato de que, para números binários arbitrários, não há muitos que começam com uma sequência muito longa de zeros. Na verdade, para qualquer largura de bit, metade de todos os inteiros começam com 1 e um quarto deles começam com 01 . Veja onde estou chegando? Meu argumento é que uma varredura linear começando da posição do bit mais significativo para o menos significativo (da esquerda para a direita) não é tão "linear" como pode parecer à primeira vista.
Pode ser mostrado 1 , que para qualquer largura de bit, o número médio de bits que precisam ser testados é no máximo 2. Isso se traduz em uma complexidade de tempo amortizado de O (1) em relação ao número de bits (!) .
Claro, o pior caso ainda é O (n) , pior do que o O (log (n)) que você obtém com abordagens do tipo busca binária, mas como há tão poucos casos piores, eles são insignificantes para a maioria dos aplicativos ( Atualizar : não é bem assim: pode haver poucos, mas podem ocorrer com alta probabilidade - consulte a atualização abaixo).
Aqui está a abordagem "ingênua" que criei, que pelo menos na minha máquina supera a maioria das outras abordagens (esquemas de pesquisa binária para ints de 32 bits sempre requerem log 2 (32) = 5 etapas, enquanto este algoritmo bobo requer menos de 2 em média) - desculpe por ser C ++ e não C puro:
Atualização : Embora o que escrevi aqui seja perfeitamente verdadeiro parainteiros arbitrários , onde cada combinação de bits é igualmente provável (meu teste de velocidade simplesmente mediu quanto tempo levou para determinar o MSB para todos os inteiros de 32 bits), inteiros da vida real, para que tal função será chamada, geralmente segue um padrão diferente: No meu código, por exemplo, esta função é usada para determinar se o tamanho de um objeto é uma potência de 2, ou para encontrar a próxima potência de 2 maior ou igual a um tamanho do objeto . Meu palpite é que a maioria dos aplicativos que usam o MSB envolvem números que são muito menores do que o número máximo que um inteiro pode representar (os tamanhos dos objetos raramente utilizam todos os bits em um size_t) Nesse caso, minha solução terá um desempenho pior do que uma abordagem de pesquisa binária - então, a última provavelmente deve ser preferida, embora minha solução seja um loop mais rápido por todos os inteiros.
TL; DR: Os inteiros da vida real provavelmente terão uma tendência para o pior caso desse algoritmo simples, o que tornará seu desempenho pior no final - apesar do fato de ser O (1) amortizado para inteiros verdadeiramente arbitrários.
1 O argumento é assim (rascunho): Seja n o número de bits (largura de bits). Há um total de 2 n inteiros que podem ser representados com n bits. Existem 2 n - 1 inteiros começando com 1 (o primeiro 1 é fixo, os n - 1 bits restantes podem ser qualquer coisa). Esses inteiros requerem apenas uma interação do loop para determinar o MSB. Além disso, há 2 n - 2 inteiros começando com 01 , exigindo 2 iterações, 2 n - 3 inteiros começando com 001 , exigindo 3 iterações e assim por diante.
Se somarmos todas as iterações necessárias para todos os inteiros possíveis e dividi-los por 2 n , o número total de inteiros, obtemos o número médio de iterações necessárias para determinar o MSB para inteiros de n bits:
(1 * 2 n - 1 + 2 * 2 n - 2 + 3 * 2 n - 3 + ... + n) / 2 n
Esta série de iterações médias é convergente e tem um limite de 2 para n até o infinito
Assim, o algoritmo ingênuo da esquerda para a direita tem, na verdade, uma complexidade de tempo constante amortizada de O (1) para qualquer número de bits.
fonte
c99nos deu
log2
. Isso elimina a necessidade de todas aslog2
implementações de molhos especiais que você vê nesta página. Você pode usar alog2
implementação do padrão assim:Um
n
de0UL
precisa ser evitado também, porque:Eu escrevi um exemplo com esse cheque que é definido arbitrariamente
Index
comoULONG_MAX
aqui: https://ideone.com/u26vsio estúdio visualo corolário da única resposta gcc do efemiente é:
A documentação para
_BitScanReverse
estados queIndex
são:Na prática, eu descobri que, se
n
é0UL
queIndex
está definido para0UL
, assim como seria para umn
de1UL
. Mas a única coisa garantida na documentação no caso de umn
de0UL
é que a devolução é:Assim, de forma semelhante à
log2
implementação preferencial acima, o retorno deve ser verificado definindoIndex
um valor sinalizado neste caso. Novamente escrevi um exemplo de usoULONG_MAX
para este valor de sinalizador aqui: http://rextester.com/GCU61409fonte
_BitScanReverse
retorna 0 apenas se a entrada foi0
. É como aBSR
instrução x86 , que configura ZF com base apenas na entrada, não na saída. Interessante que o MS diz que os documentos deixam porindex
definir quando nenhum1
bit é encontrado; que corresponde ao comportamento do conjunto x86 debsr
também. (A AMD documenta deixando o registro de destino sem modificações em src = 0, mas a Intel apenas diz saída indefinida, embora suas CPUs implementem o comportamento de deixar sem modificações.) Isso é diferente do x86lzcnt
, que dá32
para não encontrado._BitScanReverse
usa indexação baseada em zero, portanto, sen
for 1, o índice do bit definido é de fato 0. Infelizmente, como você diz sen
for 0, a saída também é 0 :( Isso significa que não há como usar o retorno para distinguir entren
1 ou 0. Era isso que eu estava tentando comunicar. Você acha que há uma maneira melhor de dizer isso?Index
. Esse não é o valor de retorno . Ele retorna um booleano que é falso se a entrada for zero (e é por isso que Index é passado por referência em vez de ser retornado normalmente). godbolt.org/g/gQKJdE . E eu verifiquei: apesar do texto dos documentos do MS,_BitScanReverse
não deixa o Index indefinidon==0
: você apenas obtém o valor que estava no registro que ele usou. (Que no seu caso foi provavelmente o mesmo registro usadoIndex
posteriormente, levando a você ver a0
).log2
desde C99.Pense em operadores bit a bit.
Eu não entendi a pergunta da primeira vez. Você deve produzir um int com o conjunto de bits mais à esquerda (os outros zero). Supondo que cmp esteja definido com esse valor:
fonte
8
deveria serCHAR_BIT
. É muito improvável que esse seja o caminho mais rápido, porque a previsão incorreta do desvio acontecerá ao sair do loop, a menos que seja usado com a mesma entrada repetidamente. Além disso, para pequenas entradas (muitos zeros), ele precisa fazer muitos loops. É como a forma alternativa que você usaria como a versão fácil de verificar em um teste de unidade para comparar com as versões otimizadas.Expandindo o benchmark de Josh ... pode-se melhorar o CLZ da seguinte maneira
Com relação ao asm: observe que existem bsr e bsrl (esta é a versão "longa"). o normal pode ser um pouco mais rápido.
fonte
Observe que o que você está tentando fazer é calcular o inteiro log2 de um inteiro,
Observe que você pode tentar pesquisar mais de 1 bit por vez.
Esta abordagem usa uma pesquisa binária
Outro método de pesquisa binária, talvez mais legível,
E porque você vai querer testá-los,
fonte
Colocar isso, visto que é "mais uma" abordagem, parece ser diferente de outras já fornecidas.
retorna
-1
ifx==0
, caso contráriofloor( log2(x))
(resultado máximo 31)Reduza o problema de 32 para 4 bits e, em seguida, use uma tabela. Talvez deselegante, mas pragmático.
É o que eu uso quando não quero usar
__builtin_clz
devido a problemas de portabilidade.Para torná-lo mais compacto, pode-se usar um loop para reduzir, adicionando 4 a r de cada vez, no máximo 7 iterações. Ou algum híbrido, como (para 64 bits): loop para reduzir para 8, teste para reduzir para 4.
fonte
Uau, foram muitas as respostas. Não lamento responder a uma pergunta antiga.
Esta resposta é muito semelhante a outra resposta ... tudo bem.
fonte
1<<k
é um toque agradável. E as máscaras?(1 << (1<<k-1)-1<< (1<<k-1)
? (most optimal
? Você compara um superlativo?)&
e&~
.) Você pode substituir as constantes hexadecimais por semelhantes((type)1<<(1<<k))-1<<(1<<k)
.O código:
Ou obtenha a parte inteira da instrução FPU FYL2X (Y * Log2 X) configurando Y = 1
fonte
double
trocadilho de tipo .) Ele usa matemática de endereço para recarregar apenas os 32 bits altos do , o que provavelmente é bom se realmente armazenar / recarregar em vez de trocadilho de alguma outra maneira, por exemplo, com umamovq
instrução como a que você pode obter aqui no x86.[7FFFFFFFFFFFFE00 - 7FFFFFFFFFFFFFFF]
.Outro pôster forneceu uma tabela de consulta usando uma consulta de todos os bytes . Caso você queira obter um pouco mais de desempenho (ao custo de 32K de memória em vez de apenas 256 entradas de pesquisa), aqui está uma solução usando uma tabela de pesquisa de 15 bits , em C # 7 para .NET .
A parte interessante é inicializar a tabela. Como é um bloco relativamente pequeno que queremos durante o tempo de vida do processo, aloco memória não gerenciada para isso usando
Marshal.AllocHGlobal
. Como você pode ver, para desempenho máximo, todo o exemplo é escrito como nativo:A tabela requer inicialização única por meio do código acima. É somente leitura, portanto, uma única cópia global pode ser compartilhada para acesso simultâneo. Com esta tabela, você pode consultar rapidamente o log 2 do inteiro , que é o que estamos procurando aqui, para todas as várias larguras de inteiro (8, 16, 32 e 64 bits).
Observe que a entrada da tabela para
0
, o único inteiro para o qual a noção de 'bit de conjunto mais alto' é indefinido, recebe o valor-1
. Essa distinção é necessária para o tratamento adequado de palavras superiores com valor 0 no código a seguir. Sem mais delongas, aqui está o código para cada um dos vários primitivos inteiros:versão ulong (64 bits)
Versão uint (32 bits)
Várias sobrecargas para o acima
Esta é uma solução completa e funcional que representa o melhor desempenho no .NET 4.7.2 para inúmeras alternativas que comparei com um equipamento de teste de desempenho especializado. Alguns deles são mencionados abaixo. Os parâmetros de teste foram uma densidade uniforme de todas as posições de 65 bits, ou seja, 0 ... 31/63 mais o valor
0
(que produz o resultado -1). Os bits abaixo da posição do índice de destino foram preenchidos aleatoriamente. Os testes foram x64 apenas , modo de lançamento, com otimizações JIT habilitadas.Esse é o fim da minha resposta formal aqui; o que se segue são algumas notas casuais e links para o código-fonte para candidatos de teste alternativos associados ao teste que executei para validar o desempenho e a exatidão do código acima.
A versão fornecida acima, codificada como Tab16A, foi uma vencedora consistente em muitas execuções. Esses vários candidatos, em forma ativa de trabalho / scratch, podem ser encontrados aqui , aqui e aqui .
Notável é que o péssimo desempenho de
ntdll.dll!RtlFindMostSignificantBit
via P / Invoke:É realmente uma pena, porque aqui está toda a função real:
Não consigo imaginar o desempenho ruim originado com essas cinco linhas, então as penalidades de transição gerenciada / nativa devem ser as culpadas. Também fiquei surpreso que o teste realmente favoreceu as
short
tabelas de pesquisa direta de 32 KB (e 64 KB) (16 bits) em relação às tabelas de pesquisa de 128 bytes (e 256 bytes)byte
(8 bits). Achei que o seguinte seria mais competitivo com as pesquisas de 16 bits, mas o último superou consistentemente isso:A última coisa que vou apontar é que fiquei bastante chocado porque meu método deBruijn não se saiu melhor. Este é o método que eu estava usando amplamente anteriormente:
Há muita discussão sobre como os métodos deBruijn são superiores e excelentes nessa questão do SO , e eu tendia a concordar. Minha especulação é que, embora os métodos deBruijn e de tabela de pesquisa direta (que descobri ser mais rápidos) tenham que fazer uma pesquisa de tabela e ambos tenham ramificações mínimas, apenas o deBruijn tem uma operação de multiplicação de 64 bits. Eu apenas testei as
IndexOfMSB
funções aqui - não o deBruijn -IndexOfLSB
mas espero que o último tenha uma chance muito melhor, já que tem muito menos operações (veja acima), e provavelmente continuarei a usá-lo para LSB.fonte
Meu método humilde é muito simples:
MSB (x) = INT [Log (x) / Log (2)]
Tradução: O MSB de x é o valor inteiro de (Log da Base x dividido pelo Log da Base 2).
Isso pode ser facilmente e rapidamente adaptado a qualquer linguagem de programação. Experimente na sua calculadora para ver por si mesmo se funciona.
fonte
int(math.log((1 << 48) - 1) / math.log(2))
é 48.Aqui está uma solução rápida para C que funciona no GCC e no Clang ; pronto para ser copiado e colado.
E uma versão um pouco melhorada para C ++ .
O código assume que
value
não será0
. Se você deseja permitir 0, você precisa modificá-lo.fonte
Presumo que sua pergunta seja para um número inteiro (chamado v abaixo) e não um número inteiro sem sinal.
Se quiser que funcione sem levar em conta o sinal, você pode adicionar um extra 'v << = 1;' antes do loop (e altere o valor de r para 30 de acordo). Por favor, me avise se eu esqueci alguma coisa. Não testei, mas deve funcionar bem.
fonte
v <<= 1
é um comportamento indefinido (UB) quandov < 0
.0x8000000
, talvez você queira dizer um 0 a mais aqui.