Кресты / Говнокод #22500 Ссылка на оригинал

0

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
inline int8_t log2(size_t value)
{
    static constexpr int8_t table[64] =
    {
        63,  0, 58,  1, 59, 47, 53,  2,
        60, 39, 48, 27, 54, 33, 42,  3,
        61, 51, 37, 40, 49, 18, 28, 20,
        55, 30, 34, 11, 43, 14, 22,  4,
        62, 57, 46, 52, 38, 26, 32, 41,
        50, 36, 17, 19, 29, 10, 13, 21,
        56, 45, 25, 31, 35, 16,  9, 12,
        44, 24, 15,  8, 23,  7,  6,  5
    };
    value |= value >> 1;
    value |= value >> 2;
    value |= value >> 4;
    value |= value >> 8;
    value |= value >> 16;
    value |= value >> 32;
    return table[((value - (value >> 1)) * 0x07EDD5E59A4E28C2) >> 58];
}

Случайно наткнулся на такую магию.
Вроде проще способ был - без массивов и плясок с умножением на магическую константу с переполнением.

Запостил: gammaker gammaker, (Updated )

Комментарии (33) RSS

  • Мне лень разбирать смысл всей этой хуерды с таблицей поиска и сдвигами, но судя по всему это логарифм по основанию два, а чтобы узнать логарифм по основанию два у unsigned целого, можно посчитать число нулей перед первым единичным битом, и я могу точно сказать, что
    https://en.wikipedia.org/wiki/Find_first_set#Tool_and_library_support
    https://www.go4expert.com/articles/builtin-gcc-functions-builtinclz-t29238/
    https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html


    Built-in Function: int __builtin_clz (unsigned int x)

    Returns the number of leading 0-bits in x, starting at the most significant bit position. If x is 0, the result is undefined.

    Built-in Function: int __builtin_clzl (unsigned long)

    Similar to __builtin_clz, except the argument type is unsigned long.

    Built-in Function: int __builtin_clzll (unsigned long long)

    Similar to __builtin_clz, except the argument type is unsigned long long.
    Ответить
    • А я всё-таки попытаюсь разобраться в этом говне.

      Сдвиги забивают все младшие биты единицами.
      0b00010101 превращается в
      0b00011111.

      Разность оставляет установленным только один бит:
      0b00011111
      -
      0b00001111
      =
      0b00010000

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

      Умножение на магическую константу числа, у которого установлен только один бит, эквивалентен сдвигу магической константы.
      0b00010000 * 0x07EDD5E59A4E28C2 == 0x07EDD5E59A4E28C2 << 4.
      Для произвольного value получим 0x07EDD5E59A4E28C2 << log_2(value).
      Двигая 64-битное число вправо на 58, мы оставляем только 6 старших битов.

      Т. е. все эти манипуляции вырезают из 0x07EDD5E59A4E28C2 шесть смежных битов. Положение вырезанного куска зависит от log_2(value). А дальше происходит выборка из таблицы.

      Почему нельзя было объявить магическую константу вроде 0xFEDBCA986543210 (тогда бы и таблица не понадобилась) и использовать сдвиги, кратные четырём? Потому что 64-битной константы не хватит для хранения 64 значений (мы в неё сможем затолкать только 16). Значит, нужно либо использовать 64*6=384-битную константу, либо... придумать алгоритм зожатия, что здесь и было продемонстрировано.
      Ответить
    • > но судя по всему это логарифм по основанию два
      inline int8_t log2(size_t value)
      Да ну?
      Ответить
      • Ну да, приходится судить о назначении функции по ее названию. А то вот я могу ж какой-нибудь тангенс написать на брейнфаковых вставках(если такие завезут в какой-нибудь компилятор), и в названии функции сказать что это синус, и ведь многие могут поверить названию, никто ж не станет код на брейнфаке анализировать
        Ответить
  • #define bormand 0x07EDD5E59A4E28C2
    
    inline int8_t log2(size_t LOVE)
    {
        static constexpr int8_t HACTEHbKA[64] =
        {
            63,  0, 58,  1, 59, 47, 53,  2,
            60, 39, 48, 27, 54, 33, 42,  3,
            61, 51, 37, 40, 49, 18, 28, 20,
            55, 30, 34, 11, 43, 14, 22,  4,
            62, 57, 46, 52, 38, 26, 32, 41,
            50, 36, 17, 19, 29, 10, 13, 21,
            56, 45, 25, 31, 35, 16,  9, 12,
            44, 24, 15,  8, 23,  7,  6,  5
        };
        LOVE |= LOVE >> 1;
        LOVE |= LOVE >> 2;
        LOVE |= LOVE >> 4;
        LOVE |= LOVE >> 8;
        LOVE |= LOVE >> 16;
        LOVE |= LOVE >> 32;
        return HACTEHbKA[((LOVE - (LOVE >> 1)) * bormand) >> 58];
    }

    Вот так легче читается.
    Ответить
  • Нет, проще способа не было. Написано все правильно, за исключением того, что вовлеченные значения, в т.ч. константу надо было бы явно сделать беззнаковой. Это - классический способ, основанный на использовании DeBruijn последовательности, владение которым обязательно для каждого уважающего себя программиста.

    Разумеется, в системе команд процессоров интеловского семейства (да и других тоже) уже давно появились встроенные команды для решения этой задачи, на к которым в компиляторах привязаны соответствующие нестандартные средства: `_BitScanReverse64` в MSVC и `__builtin_clzll` в GCC. Но, разумеется, использование этих нестандартных свойств без предоставления fallback implementation не допускается: это будет классикой жырного говнокодинга. Вышеприведенный вариант как раз является хорошим вариантом fallback implementation.

    Так что эту заявку на говнокод я вынужден отклонить. Учите матчасть.
    Ответить
    • > владение которым обязательно для каждого уважающего себя программиста
      Вот ты меня унизил сейчас.
      Ответить
      • дурак!
        надо было сделать вид что ты знаешь что такое последовательность де Брёйна.

        Дескать: "ах, да я каждый день с ней сталкиваюсь когда пишу валидаторы формочек на JS"
        Ответить
    • Есть способ проще: сдвиг числа в цикле влево, пока единичка не влезет в старший бит (знаковое число станет отрицательным). Правда, этот способ будет медленнее на тех процессорах, которые умеют умножать быстро. Но в качестве тупого fallback implementation сгодится.
      Ответить
    • > Но, разумеется, использование этих нестандартных свойств без предоставления fallback implementation не допускается: это будет классикой жырного говнокодинга.

      Нет, не соглашусь. Я считаю что наилучшим вариантом будет втулить туда все возможные нестантартные интринсики через #ifdef директивы препроцессора, а если ничего из этого по каким-либо причинам не подходит, надо чтобы это просто не собиралось. И тогда уже пусть программист пилит там себе реализацию. Поясню:

      Если никакие интнинсики не подошли, скорее всего кто-то это собирает под какую-то нестандартную платформу каким-то нестандартным компилятором, например это вполне может оказаться какой-нибудь AVR микроконтроллер. Если это окажется AVR микроконтроллер без поддержки инструкций умножения, то вышеозначенная fallback implementation, использующая умножение, скорее всего развернется в очень жирный и неэффективный машинный код, что может быть критично для микроконтроллера, и лучше бы под этот случай написать другую fallback implementation, например через какие-нибудь сдвиги, или же вообще на ассемблерных вставках
      Ответить
      • dfullh <a href= http://buyviagra24ph.com >here</a>
        astandj <a href= http://buyviagra24ph.com >generic viagra</a>
        Ответить
    • > владение которым обязательно для каждого уважающего себя программиста.
      case dismissed
      Ответить
    • > TheCalligrapher

      Ого, давненько вас не было видно. Заходите почаще, а то доля интересного чтива на сайте стремится к нулю на фоне разбушевавшихся ботов.
      Ответить
  • напомнило историю про "константу Кармака", она же "быстрый обратный квадртаный корень"
    Знаете?
    Ответить
      • нет, ты не можешь быть джаваскриптером если ты знаешь что такое "старший бит"

        погугли "0x5f3759df"
        Ответить
        • Нагуглил. Прочитал и про то, что сам Кармак говорил, что алгоритм ему кто-то принёс, и про то, что для double алгоритм практически бесполезен (ибо точность первой итерации для double уже никакая, а несколько итераций метода Ньютона дадут такую же задержку, как встроенный в сопроцессор квадратный корень). И про 0x5f37642f и 0x5f375a86 тоже прочитал.

          А история-то в чём?
          Ответить
          • история ровно в том, что ты нагуглил

            что "волшебная" константа позволяет примерно посчитать обратный корень быстрее чем на fpu
            Ответить
    • А я знаю. А еще я могу физбаз на шаблонах написать.
      Ответить
      • а говорят 99% программистов не могут физбаз даже на том языке, на котором 10 лет пишут
        Ответить

Добавить комментарий

Из-за тебя ушел bormand, guest!

    А не использовать ли нам bbcode?


    8