Как сделать целое число log2 () в C++?

Как и другие списки (но не Scheme), Emacs Lisp имеет отдельные пространства имен для переменных и функций (т.е. это 'Lisp 2 ', а не 'Lisp 1 ] '; см. Технические вопросы разделения в функциональных ячейках и ячейках значений для определения происхождения и значения этих терминов).

Вам нужно будет использовать funcall или apply для вызова лямбда-функции (или другой функции), которая хранится в переменной.

(cond (test-1 (funcall do-work 'a 'b 'c))
      (test-2 (funcall do-work 'i 'j 'k))

Используйте funcall, если вы всегда будете отправлять одинаковое количество аргументов. Используйте apply, если вам нужно отправить переменное число аргументов.

Внутренний механизм состоит в том, что каждый символ имеет несколько «ячеек» . Какая ячейка используется, зависит от , где символ находится в оценочной форме . Когда символ является первым элементом оцененной формы, используется его ячейка «функции» . В любой другой позиции используется ее ячейка «значения» . В вашем коде do-work имеет функцию в своей ячейке значения. Для доступа к нему в качестве функции вы используете funcall или apply. Если бы он был в ячейке функции, вы могли бы вызвать его напрямую, используя его имя в качестве машины оценочной формы. Вы можете сделать это с помощью flet или labels из пакета cl .

41
задан Nathan Fellman 15 June 2009 в 07:49
поделиться

7 ответов

Вместо этого вы можете использовать этот метод:

int targetlevel = 0;
while (index >>= 1) ++targetlevel;

Примечание: это изменит индекс. Если он вам нужен без изменений, создайте другое временное int.

Угловой случай - когда index равен 0. Вероятно, вам следует проверить его отдельно и выбросить исключение или вернуть ошибку, если index == 0.

45
ответ дан 27 November 2019 в 00:05
поделиться

Насколько глубоко вы проецируете свое дерево? Вы можете установить диапазон, скажем ... +/- 0,00000001 для числа, чтобы заставить его принимать целочисленное значение.

На самом деле я не уверен, что вы наберете число, подобное 1.99999999, потому что ваш log2 не должен потерять точность при вычислении значений 2 ^ n (поскольку числа с плавающей запятой округляются до ближайшей степени 2).

0
ответ дан 27 November 2019 в 00:05
поделиться
int targetIndex = floor(log(i + 0.5)/log(2.0));
2
ответ дан 27 November 2019 в 00:05
поделиться

У меня никогда не было проблем с точностью с плавающей запятой в формуле, которую вы используете (и быстрой проверкой чисел от 1 до 2 31 - 1 найдено ошибок нет), но если вы беспокоитесь, вы можете вместо этого использовать эту функцию, которая возвращает те же результаты и примерно на 66% быстрее в моих тестах:

int HighestBit(int i){
    if(i == 0)
        return -1;

    int bit = 31;
    if((i & 0xFFFFFF00) == 0){
        i <<= 24;
        bit = 7;
    }else if((i & 0xFFFF0000) == 0){
        i <<= 16;
        bit = 15;
    }else if((i & 0xFF000000) == 0){
        i <<= 8;
        bit = 23;
    }

    if((i & 0xF0000000) == 0){
        i <<= 4;
        bit -= 4;
    }

    while((i & 0x80000000) == 0){
        i <<= 1;
        bit--;
    }

    return bit; 
}
3
ответ дан 27 November 2019 в 00:05
поделиться

Если вам просто нужна операция быстрого целочисленного журнала 2 , следующая функция mylog2 () сделает это, не беспокоясь о числах с плавающей запятой. точность:

#include <limits.h>

static unsigned int mylog2 (unsigned int val) {
    if (val == 0) return UINT_MAX;
    if (val == 1) return 0;
    unsigned int ret = 0;
    while (val > 1) {
        val >>= 1;
        ret++;
    }
    return ret;
}

#include <stdio.h>

int main (void) {
    for (unsigned int i = 0; i < 20; i++)
        printf ("%u -> %u\n", i, mylog2(i));
    putchar ('\n');
    for (unsigned int i = 0; i < 10; i++)
        printf ("%u -> %u\n", i+UINT_MAX-9, mylog2(i+UINT_MAX-9));
    return 0;
}

В приведенном выше коде также есть небольшая тестовая программа, поэтому вы можете проверить поведение:

0 -> 4294967295
1 -> 0
2 -> 1
3 -> 1
4 -> 2
5 -> 2
6 -> 2
7 -> 2
8 -> 3
9 -> 3
10 -> 3
11 -> 3
12 -> 3
13 -> 3
14 -> 3
15 -> 3
16 -> 4
17 -> 4
18 -> 4
19 -> 4

4294967286 -> 31
4294967287 -> 31
4294967288 -> 31
4294967289 -> 31
4294967290 -> 31
4294967291 -> 31
4294967292 -> 31
4294967293 -> 31
4294967294 -> 31
4294967295 -> 31

Он вернет UINT_MAX для входного значения 0 в качестве указания на неопределенный результат, так что что-то, что вы должны проверить (ни одно допустимое целое число без знака не будет иметь такой высокий логарифм).

Между прочим, есть несколько безумно быстрых хаков, чтобы сделать именно это (найти самый высокий бит, установленный в дополнительном числе до 2), доступных из здесь . Я бы не советовал использовать их, если скорость не важна (я сам предпочитаю удобочитаемость), но вы должны знать, что они существуют.

19
ответ дан 27 November 2019 в 00:05
поделиться

Если вы используете новейшую платформу x86 или x86-64 (а вы, вероятно, так и есть), используйте инструкцию bsr , которая вернет позицию самого высокого набора бит в целое число без знака. Оказывается, это то же самое, что и log2 (). Вот короткая функция C или C ++, которая вызывает bsr с использованием встроенного ASM:

#include <stdint.h>
static inline uint32_t log2(const uint32_t x) {
  uint32_t y;
  asm ( "\tbsr %1, %0\n"
      : "=r"(y)
      : "r" (x)
  );
  return y;
}
77
ответ дан 27 November 2019 в 00:05
поделиться

Это не стандартно и не обязательно переносимо, но в целом работает. Я не знаю, насколько это эффективно.

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

Найдите представление чисел с плавающей запятой IEEE,

2
ответ дан 27 November 2019 в 00:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: