1. C++ / Говнокод #22500

    −11

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    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, 06 Марта 2017

    Комментарии (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];
      }

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

          Ну ладно, исправил:
          static constexpr uint64_t bormand = 0x07EDD5E59A4E28C2;
          Ответить
      • Спасибо призрок борманда дал идею!
        http://govnokod.ru/22508
        милый Борманд жду тибя
        Ответить
    • Нет, проще способа не было. Написано все правильно, за исключением того, что вовлеченные значения, в т.ч. константу надо было бы явно сделать беззнаковой. Это - классический способ, основанный на использовании DeBruijn последовательности, владение которым обязательно для каждого уважающего себя программиста.

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

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

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

        Ого, давненько вас не было видно. Заходите почаще, а то доля интересного чтива на сайте стремится к нулю на фоне разбушевавшихся ботов.
        Ответить
    • The magic number from a De Bruijn sequence such as 0x07EDD5E59A4E28C2
      http://cdn.intechopen.com/pdfs-wm/5296.pdf
      Ответить
    • напомнило историю про "константу Кармака", она же "быстрый обратный квадртаный корень"
      Знаете?
      Ответить
      • Я не знаю. Я тупой яваскриптер. Мне расскажите.
        Ответить
        • нет, ты не можешь быть джаваскриптером если ты знаешь что такое "старший бит"

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

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

              что "волшебная" константа позволяет примерно посчитать обратный корень быстрее чем на fpu
              Ответить
              • А ещё есть интересные быстрые алгоритмы, основанные на волшебной константе?
                Ответить
                • понятия не имею
                  я в математике слаб
                  Ответить
                  • > математика
                    > сомнительный битодроч
                    Ответить
                    • ну за битодрочем стоит некоторая математика всё таки.
                      Ответить
      • А я знаю. А еще я могу физбаз на шаблонах написать.
        Ответить
        • а говорят 99% программистов не могут физбаз даже на том языке, на котором 10 лет пишут
          Ответить
    • > size_t
      > value |= value >> 32;
      Ай! Нога!
      Ответить

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