Как написать базу журнала (2) на c / c ++

99

Есть ли способ записать функцию журнала (база 2)?

Язык C имеет 2 встроенных функции - >>

1. logкоторый является основанием e.

2. log10база 10;

Но мне нужна функция журнала базы 2, как это вычислить.

Рассел
источник
1
Для вычислений на глазном яблоке логарифм по основанию 2 близок к логарифму по основанию 10 плюс натуральный логарифм. Очевидно, что лучше написать более точную (и более быструю) версию программы.
Дэвид Торнли
Для целых чисел вы можете выполнить цикл с правым битовым смещением и останавливаться при достижении 0. Счетчик циклов является приближением журнала
Базиль Старынкевич

Ответы:

199

Простая математика:

    журнал 2 ( x ) = журнал y ( x ) / журнал y (2)

где y может быть любым, что для стандартных функций журнала равно 10 или e .

Адам Крум
источник
56

У C99 есть log2(как log2fи log2lдля float и long double).

Мэтью Флашен
источник
53

Если вы ищете интегральный результат, вы можете просто определить самый высокий бит, установленный в значении, и вернуть его позицию.

томлогический
источник
27
Для этого также есть хороший метод бит-тидлинга (взятый из Integer.highestOneBit(int)метода Java ):i |= (i >> 1); i |= (i >> 2); i |= (i >> 4); i |= (i >> 8); i |= (i >> 16); return i - (i >>> 1);
Joey
38
... илиwhile (i >>= 1) { ++l; }
Ли Дэниел Крокер
2
@Joey Это будет работать, если целое число имеет ширину 32 бита, не так ли? Для 64-битной версии было бы доп i>>32. Но поскольку в Java есть только 32-битные целые числа, это нормально. Для C / C ++ это необходимо учитывать.
Зосо
43
#define M_LOG2E 1.44269504088896340736 // log2(e)

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

(умножение может быть быстрее деления)

Logicray
источник
1
Просто хотел уточнить - используя правила преобразования логов + тот факт, что log_2 (e) = 1 / log_e (2) -> получаем результат
Guy L
9

Как указано на http://en.wikipedia.org/wiki/Logarithm :

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

Которое значит что:

log2(x) = log10(x) / log10(2)
Патрик
источник
11
Обратите внимание, что вы можете предварительно вычислить log10 (2) для повышения производительности.
corsiKa
@Johannes: Я сомневаюсь, что компилятор предварительно вычислит log10 (2). Компилятор не знает, что log10 будет каждый раз возвращать одно и то же значение. Насколько известно компилятору, log10 (2) может возвращать разные значения при последовательных вызовах.
abelenky
@abelenky: Хорошо, я беру это обратно. Поскольку компилятор никогда не видит источник log()реализации, он этого не сделает. Виноват.
Joey
3
@abelenky: Поскольку log10()это функция, определенная в стандарте C, компилятор может обработать ее «специально», включая предварительное вычисление результата, что, как я считаю, было предложением @Johannes?
caf
1
@CarlNorum: я только что проверил, и gcc 4.7 по крайней мере заменяет log10(2)константу.
caf
8

Если вы хотите сделать это быстро, вы можете использовать таблицу поиска, как в Bit Twiddling Hacks (только целочисленный log2).

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];

Кроме того, вам следует взглянуть на встроенные методы вашего компилятора, например, _BitScanReverseкоторые могут быть быстрее, потому что они могут полностью вычисляться аппаратно.

Также обратите внимание на возможные дубликаты. Как сделать целое число log2 () в C ++?

bkausbk
источник
Почему умножение и поиск по таблице в конце? Не могли бы вы просто сделать (v + 1), которое округлило бы до следующей степени двойки? А затем вы можете перейти вправо на одну, чтобы округлить до следующей степени 2.
Сафайет Ахмед,
@SafayetAhmed Пожалуйста, опишите, как вы хотите найти log2 числа с помощью этого метода. Я не знаю более простого способа получить эту ценность. Помимо использования приведенной выше арифметики с поисковой таблицей, можно использовать итеративный / рекурсивный алгоритм или использование специального / встроенного оборудования для выполнения вычислений.
bkausbk
Скажем, биты 32-битной переменной v пронумерованы от 0 (LSB) до N (MSB). Скажем, старший бит набора v равен n. Было бы правильно сказать, что n представляет пол (log2 (v))? Разве вам не интересно просто найти n с учетом v?
Сафайет Ахмед,
Я понял, что то, что я описал, даст вам ближайшую наименьшую степень двойки, а не фактический логарифм. Умножение и поиск в таблице предназначены для перехода от степени двойки к логарифму. Вы сдвигаете число 0x07C4ACDD влево на некоторую величину. Величина сдвига влево будет зависеть от степени двойки. Число таково, что любая последовательная последовательность из 5 бит уникальна. (0000 0111 1100 0100 0110 1100 1101 1101) дает вам последовательности (00000) (00001) ... (11101). В зависимости от того, насколько далеко вы сдвинетесь влево, вы получите один из этих 5-битных шаблонов. Затем поиск по таблице. Очень хорошо.
Сафайет Ахмед,
3
log2(x) = log10(x) / log10(2)
пустота
источник
Проголосуйте за простоту, ясность и предоставление кода на основе информации, предоставленной 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;
 }

В основном то же, что и у tomlogic .

Устаман Сангат
источник
1
В этом решении есть несколько ошибок, но в целом это хорошо, если вы хотите избежать операций с плавающей запятой. Вы полагаетесь на переполнение, чтобы это сработало, поскольку вы инициализируете целое число без знака с -1. Это можно исправить, установив его на 0 и затем вернув значение - 1, если вы проверяете регистр 0, что вы и делаете. Другая проблема заключается в том, что вы полагаетесь на остановку цикла, когда n == 0, что вы должны указать явно. Кроме того, это замечательно, если вы хотите избежать плавающих точек.
Риан Куинн
2

Вы должны включить math.h (C) или cmath (C ++). Конечно, имейте в виду, что вы должны следовать математике, которую мы знаем ... только числа> 0.

Пример:

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

int main(){
    cout<<log2(number);
}
user2143904
источник
2

Мне нужно было иметь большую точность, чем просто положение самого старшего бита, а в микроконтроллере, который я использовал, не было математической библиотеки. Я обнаружил, что простое использование линейного приближения между значениями 2 ^ n для аргументов с положительным целым числом работает хорошо. Вот код:

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));
}

В моей основной программе мне нужно было вычислить N * log2 (N) / 2 с целочисленным результатом:

temp = (((uint32_t) N) * приблизительно_log_base_2_N_times_256) / 512;

и все 16-битные значения никогда не отклонялись более чем на 2%

Дэвид Рейнагель
источник
1

Все приведенные выше ответы верны. Этот мой ответ ниже может быть полезен, если кому-то это нужно. Я видел это требование во многих вопросах, которые мы решаем с помощью C.

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

Однако, если вы используете язык C и хотите получить целочисленный результат, вы можете использовать следующее:

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

Надеюсь это поможет.

Мажар МИК
источник
0

Проконсультируйтесь с вашим базовым курсом математики log n / log 2. Неважно, выберете вы logили, log10в этом случае, деление на logновую базу дает свое.

Питер
источник
0

Улучшенная версия того, что сделал Устаман Сангат

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

    return val;
}
Риан Куинн
источник