Como escrever base de log (2) em c / c ++

98

Existe alguma maneira de escrever a função de log (base 2)?

A linguagem C tem 2 funções integradas - >>

1. logque é a base e.

2. log10base 10;

Mas preciso da função log da base 2. Como calcular isso.

Russell
fonte
1
Para cálculos do globo ocular, o logaritmo de base 2 é quase igual ao logaritmo de base 10 mais o logaritmo natural. Obviamente, é melhor escrever uma versão mais precisa (e mais rápida) em um programa.
David Thornley
Para inteiros, você pode fazer um loop no bithift direito e parar quando chegar a 0. A contagem do loop é uma aproximação do log
Basile Starynkevitch

Respostas:

198

Matemática simples:

    log 2 ( x ) = log y ( x ) / log y (2)

onde y pode ser qualquer coisa, que para funções de log padrão é 10 ou e .

Adam Crume
fonte
56

C99 tem log2(assim como log2fe log2lpara float e long double).

Matthew Flaschen
fonte
53

Se você está procurando um resultado integral, pode apenas determinar o bit mais alto definido no valor e retornar sua posição.

tomlogic
fonte
27
Há também um bom método de ajuste de bits para isso (tirado do Integer.highestOneBit(int)método Java ):i |= (i >> 1); i |= (i >> 2); i |= (i >> 4); i |= (i >> 8); i |= (i >> 16); return i - (i >>> 1);
Joey
38
... ouwhile (i >>= 1) { ++l; }
Lee Daniel Crocker
2
@Joey Isso funcionaria assumindo que o inteiro tem 32 bits de largura, não? Para 64 bits teria um extra i>>32. Mas, como o Java tem apenas ints de 32 bits, tudo bem. Para C / C ++, isso precisa ser considerado.
Zoso
43
#define M_LOG2E 1.44269504088896340736 // log2(e)

inline long double log2(const long double x){
    return log(x) * M_LOG2E;
}

(a multiplicação pode ser mais rápida do que a divisão)

Logicray
fonte
1
Só queria esclarecer - usando as regras de conversão de log + o fato de que log_2 (e) = 1 / log_e (2) -> obtemos o resultado
Guy L
16
log2(int n) = 31 - __builtin_clz(n)
w00t
fonte
9

Conforme declarado em http://en.wikipedia.org/wiki/Logarithm :

logb(x) = logk(x) / logk(b)

O que significa que:

log2(x) = log10(x) / log10(2)
Patrick
fonte
11
Observe que você pode pré-calcular o log10 (2) para aumentar o desempenho.
corsiKa
@Johannes: Duvido que o compilador pré-calcule o log10 (2). O compilador não sabe que log10 retornará o mesmo valor todas as vezes. Até onde o compilador sabe, log10 (2) poderia retornar valores diferentes em chamadas sucessivas.
Abelenky
@abelenky: Ok, retiro o que disse. Como o compilador nunca vê o código-fonte da log()implementação, ele não o fará. Foi mal.
Joey
3
@abelenky: Como log10()uma função é definida no padrão C, o compilador está livre para tratá-la "especialmente", incluindo pré-computar o resultado, o que acredito ter sido a sugestão de @Johannes?
caf de
1
@CarlNorum: Acabei de verificar e o gcc 4.7 pelo menos substitui log10(2)por uma constante.
caf
8

Se você quiser torná-lo mais rápido, você pode usar uma tabela de pesquisa como em Bit Twiddling Hacks (log2 inteiro apenas).

uint32_t v; // find the log base 2 of 32-bit v
int r;      // result goes here

static const int MultiplyDeBruijnBitPosition[32] = 
{
  0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
  8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
};

v |= v >> 1; // first round down to one less than a power of 2 
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;

r = MultiplyDeBruijnBitPosition[(uint32_t)(v * 0x07C4ACDDU) >> 27];

Além disso, você deve dar uma olhada nos métodos embutidos do seu compilador, como o _BitScanReversequal pode ser mais rápido porque pode ser inteiramente computado em hardware.

Dê uma olhada também nas possíveis duplicatas. Como fazer um log2 () inteiro em C ++?

bkausbk
fonte
Por que a multiplicação e a consulta à tabela no final? Você não poderia simplesmente fazer (v + 1) que arredondaria para a próxima potência de dois? E então, você pode mudar para a direita em um para arredondar para a próxima potência de 2.
Safayet Ahmed
@SafayetAhmed Descreva como deseja localizar o log2 de um número com esse método. Não conheço uma maneira mais fácil de obter esse valor. Além de usar a aritmética acima com a tabela de pesquisa, pode-se usar um algoritmo iterativo / recursivo ou usar hardware dedicado / embutido para fazer o cálculo.
bkausbk
Digamos que os bits de uma variável de 32 bits v são numerados de 0 (LSB) a N (MSB). Digamos que o conjunto de bits mais significativo de v seja n. Seria correto dizer que n representa floor (log2 (v))? Você não está interessado em apenas encontrar n dado v?
Safayet Ahmed
Percebi que o que descrevi apenas forneceria a potência mais baixa de 2 mais próxima e não o logaritmo real. A multiplicação e a consulta à tabela servem para passar da potência de dois ao logaritmo. Você está mudando o número 0x07C4ACDD deixado por alguma quantia. A quantidade que você desloca para a esquerda dependerá da potência de dois. O número é tal que qualquer sequência consecutiva de 5 bits é única. (0000 0111 1100 0100 0110 1100 1101 1101) fornece as sequências (00000) (00001) ... (11101). Dependendo de quão distante você desloca para a esquerda, você obtém um desses padrões de 5 bits. Em seguida, consulta de tabela. Muito agradável.
Safayet Ahmed
3
log2(x) = log10(x) / log10(2)
o vazio
fonte
Vote positivamente para simplicidade, clareza e fornecimento de código com base nas informações fornecidas do OP.
yoyo
3
uint16_t log2(uint32_t n) {//but truncated
     if (n==0) throw ...
     uint16_t logValue = -1;
     while (n) {//
         logValue++;
         n >>= 1;
     }
     return logValue;
 }

Basicamente o mesmo do tomlogic .

Ustaman Sangat
fonte
1
Existem algumas coisas erradas com esta solução, mas em geral, isso é bom se você quiser evitar pontos flutuantes. Você está contando com o estouro para que isso funcione, pois está inicializando um inteiro não assinado com -1. Isso poderia ser corrigido inicializando-o com 0 e, em seguida, retornando o valor -1, assumindo que você verifique o caso 0, o que você faz. O outro problema é que você está contando com a parada do loop quando n == 0, o que você deve declarar explicitamente. Fora isso, isso é ótimo se você quiser evitar pontos flutuantes.
Rian Quinn
2

Você tem que incluir math.h (C) ou cmath (C ++). Claro, lembre-se de que você deve seguir a matemática que conhecemos ... apenas números> 0.

Exemplo:

#include <iostream>
#include <cmath>
using namespace std;

int main(){
    cout<<log2(number);
}
user2143904
fonte
2

Eu precisava ter mais precisão do que apenas a posição do bit mais significativo, e o microcontrolador que estava usando não tinha biblioteca matemática. Descobri que apenas usar uma aproximação linear entre 2 ^ n valores para argumentos de valor inteiro positivo funcionou bem. Aqui está o código:

uint16_t approx_log_base_2_N_times_256(uint16_t n)
{
    uint16_t msb_only = 0x8000;
    uint16_t exp = 15;

    if (n == 0)
        return (-1);
    while ((n & msb_only) == 0) {
        msb_only >>= 1;
        exp--;
    }

    return (((uint16_t)((((uint32_t) (n ^ msb_only)) << 8) / msb_only)) | (exp << 8));
}

Em meu programa principal, precisei calcular N * log2 (N) / 2 com um resultado inteiro:

temp = (((uint32_t) N) * approx_log_base_2_N_times_256) / 512;

e todos os valores de 16 bits nunca foram desativados em mais de 2%

David Reinagel
fonte
1

Todas as respostas acima estão corretas. Esta minha resposta abaixo pode ser útil se alguém precisar dela. Eu vi esse requisito em muitas questões que estamos resolvendo usando C.

log2 (x) = logy (x) / logy (2)

No entanto, se estiver usando a linguagem C e quiser o resultado em número inteiro, você pode usar o seguinte:

int result = (int)(floor(log(x) / log(2))) + 1;

Espero que isto ajude.

Mazhar MIK
fonte
0

Consulte o seu curso de matemática básica log n / log 2,. Não importa se você escolhe logou log10, neste caso, dividir pelo valor logda nova base resolve.

Pieter
fonte
0

Versão aprimorada do que Ustaman Sangat fez

static inline uint64_t
log2(uint64_t n)
{
    uint64_t val;
    for (val = 0; n > 1; val++, n >>= 1);

    return val;
}
Rian Quinn
fonte