Как фундаментальная математика эффективно оценивается языками программирования?

22

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

Q : Как это работает?

A : Потому что это так!

Я ненавижу это понимание! Я люблю знания, и, кроме того, я люблю учиться, что приводит меня к моему вопросу (хотя и широкому).

Вопрос:

Как фундаментальные математические операторы оцениваются с помощью языков программирования?

Как современные методы были улучшены?

пример

var = 5 * 5; 

Моя интерпретация:

$num1 = 5; $num2 = 5; $num3 = 0;
while ($num2 > 0) {
    $num3 = $num3 + $num1;
    $num2 = $num2 - 1;
}
echo $num3;

Это кажется крайне неэффективным. При более высоких коэффициентах этот метод очень медленный, в то время как стандартный встроенный метод является мгновенным. Как бы вы имитировали умножение без повторения сложения?

var = 5 / 5;

Как это вообще сделано? Я не могу придумать способ буквально разделить его на 5 равных частей.

var = 5 ^ 5; 

Итерации итераций сложения? Моя интерпретация:

$base = 5;
$mod = 5;
$num1 = $base;
while ($mod > 1) {

    $num2 = 5; $num3 = 0;
    while ($num2 > 0) {
        $num3 = $num3 + $num1;
        $num2 = $num2 - 1;
    }
    $num1 = $num3;
    $mod -=1;
}
echo $num3;

Опять же, это крайне неэффективно, но я не могу придумать другого способа сделать это. Этот же вопрос распространяется на все математические связанные функции, которые обрабатываются автоматически.

Корвин Санто
источник
1
Немного предыстории обо мне, я иду в колледж для информатики, а затем в математической теории жизни, а также, возможно, философии и теоретической физики. Много устремлений, мало времени.
Корвин Санто
10
Можно ли предположить, что вы просмотрели все ссылки с en.wikipedia.org/wiki/Category:Computer_arithmetic ?
JB King
2
По сути, это похоже на то, как вас учили делать многозначное умножение и длинное деление в начальной школе. Возьмите одну цифру A, умножьте на B. Сдвиньте на десять. Возьмите следующую цифру A, умножьте на B. Повторите для всех цифр, сложите все вместе. Поскольку это двоичное, умножение одной цифры проще (или x0, или x1), и вместо сдвига на десять вы удваиваете. Разделение аналогично.
Спросите о Монике

Ответы:

35

Чтобы действительно понять, как работает арифметика внутри компьютера, вам нужно запрограммировать на ассемблере. Предпочтительно один с маленьким размером слова и без инструкций умножения и деления. Что-то вроде 6502.

На 6502 практически вся арифметика выполняется в регистре под названием «Аккумулятор». (Регистр - это специальная ячейка памяти внутри процессора, к которой можно быстро получить доступ.) Таким образом, чтобы добавить два числа, вы загружаете первое число в накопитель, а затем добавляете второе число к нему.

Но это упрощает. Поскольку 6502 - это 8-разрядный процессор, он может обрабатывать числа только от 0 до 255. Большую часть времени вы захотите работать с большими числами. Вы должны добавить их порциями по 8 бит за раз. У процессора есть флаг Carry, который устанавливается, когда результат добавления двух чисел переполняет Аккумулятор. Процессор добавляет, что при выполнении сложения его можно использовать для «переноса 1», предполагая, что вы начинаете с младшего байта числа. Многобайтовое добавление на 6502 выглядит так:

  1. Очистить флаг переноса (CLC)
  2. Загрузить младший байт первого числа (LDA, аккумулятор нагрузки)
  3. Добавить младший байт второго номера (ADC, добавить с переносом)
  4. Сохранять младший байт результата (STA, накопитель)
  5. Повторите шаги 2-4 с последовательными байтами более высокого порядка
  6. Если в конце перенос будет установлен, вы переполнились; предпринять соответствующие действия, такие как создание сообщения об ошибке (BCS / BCC, ответвление, если перенос установлен / сброшен)

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

Но ждать! А как насчет отрицательных чисел? Ну, с 6502 они хранятся в формате, называемом дополнением до двух. Предполагая 8-битное число, -1 сохраняется как 255, потому что, когда вы добавляете 255 к чему-либо, вы получаете на единицу меньше в Аккумуляторе (плюс перенос). -2 хранится как 254 и так далее, вплоть до -128, который хранится как 128. Таким образом, для целых чисел со знаком половина диапазона байта 0-255 используется для положительных чисел и половина для отрицательных чисел. (Это соглашение позволяет вам просто проверить старший бит числа, чтобы увидеть, является ли он отрицательным.)

Думайте об этом как о 24-часовых часах: добавление 23 к времени приведет к времени на час раньше (на следующий день). Таким образом, 23 является модульным эквивалентом часов -1.

Когда вы используете более 1 байта, вы должны использовать большие числа для негативов. Например, 16-битные целые числа имеют диапазон 0-65536. Таким образом, 65535 используется для представления -1, и так далее, потому что добавление 65535 к любому числу приводит к одному меньшему (плюс перенос).

На 6502 есть только четыре арифметических действия: сложение, вычитание, умножение на два (сдвиг влево) и деление на два (сдвиг вправо). Умножение и деление могут быть выполнены с использованием только этих операций при работе в двоичном формате. Например, рассмотрим умножение 5 (двоичное число 101) и 3 (двоичное число 11). Как и в случае десятичного длинного умножения, мы начинаем с правой цифры множителя и умножаем 101 на 1, давая 101. Затем мы сдвигаем умножение влево и умножаем 1010 на 1, давая 1010. Затем мы складываем эти результаты вместе, давая 1111, или 15. Поскольку мы когда-либо умножаем только на 1 или 0, мы на самом деле не умножаемся; каждый бит множителя просто служит в качестве флага, который говорит нам, следует ли добавить (сдвинутый) множитель или нет.

Деление аналогично ручному делению с использованием пробных делителей, кроме двоичного. Если вы делите на константу, это можно сделать способом, аналогичным вычитанию: вместо того, чтобы делить на X, вы умножаетесь на предварительно вычисленное отображение 1 / X, которое дает желаемый результат плюс переполнение. Даже сегодня это быстрее, чем разделение.

Теперь попробуйте выполнить математику с плавающей запятой в сборке или преобразовать числа с плавающей запятой в хорошие выходные форматы в сборке. И помните, это 1979 год, а тактовая частота равна 1 МГц, поэтому вы должны делать это максимально эффективно.

Сегодня все по-прежнему работает примерно так, за исключением больших размеров слов и большего количества регистров, и, конечно, большая часть математики выполняется аппаратно. Но это все так же принципиально. Если вы сложите, например, количество сдвигов и сложений, необходимых для умножения, это довольно хорошо коррелирует с количеством циклов, необходимых для инструкции аппаратного умножения на ранних процессорах, имеющих такую ​​инструкцию, например, 6809, где она была выполнена в микрокоде почти так же, как если бы вы делали это вручную. (Если у вас больше транзисторный бюджет, есть более быстрые способы сдвигов и добавлений, поэтому современные процессоры не выполняют эти операции последовательно и могут выполнять умножения всего за один цикл.)

Kindall
источник
3
Эй, спасибо за ваше очень подробное объяснение! Это именно то, что я хотел! На моем уровне вы часто забываете, что то, что поддерживает вас, как правило, сложнее, чем все, что вы делаете. Именно поэтому я хочу изучать информатику. Я ненавижу тот факт, что если бы я вернулся в прошлое, я не знал бы ничего, что изменило бы мир, просто как сформулировать правильное выражение SQL;) В любом случае, большое спасибо за то, что потратили время на написание этого ответа, Вы дали мне тестер вкуса в то, что я собираюсь вникать.
Корвин Санто
7
не согласен, сборка все еще слишком высока, если вы хотите знать, как компьютеры выполняют арифметику, вам нужно взглянуть на аппаратные средства или, по крайней мере, на аппаратные алгоритмы
jk.
Эх. Как только вы узнаете, что есть сумматоры и переключатели, вы можете легко представить, что они управляются аппаратно, как программно, и легче играть с программным обеспечением.
любезно
4
-1. Аппаратное умножение не было выполнено со сменами и добавками в течение почти 3 десятилетий, и многие процессоры могут выполнять умножение за один цикл. Проверьте статью в Википедии на Binary Multiplierдетали.
Мейсон Уилер
24

В конце основные арифметические операции выполняются аппаратно. Более конкретно, в процессоре (или фактически его части)

Другими словами, это электронные схемы. Установите соответствующие биты в качестве входных данных, и вы получите соответствующие биты в качестве выходных данных. Это комбинация основных логических элементов.

http://en.wikipedia.org/wiki/Adder_%28electronics%29

http://en.wikipedia.org/wiki/Binary_multiplier

dagnelies
источник
3
Алгоритмы для аппаратного обеспечения тщательно определены и могут быть изучены отдельно от аппаратного обеспечения.
С.Лотт
@ S.Lott: я нахожу ваш комментарий запутанным. Для меня алгоритмы включают в себя последовательность шагов, которые вы выполняете, процедуру, то, что вы можете запрограммировать. Здесь речь идет об электронных схемах, выполняющих основные арифметические операции. Другими словами, просто последовательность ворот, по которым течет ток. Так что в лучшем случае это скорее «логично», чем «алгоритмически». ... мои 2 цента.
dagnelies
6
Алгоритм является «конечным, определенным и эффективным». Он может быть в цепях, или с помощью бумаги и карандаша, или с помощью Tinkertoys или молекул в чашке или ДНК. Алгоритм может быть любым. Электронная схема должна следовать определенному алгоритму. Это волшебным образом не избавляет от необходимости в алгоритмах.
S.Lott
1
Будет ли процесс, состоящий только из одного шага, рассматриваться как «алгоритм»? FWIW, электронные схемы обычно следуют таблице истинности - одношаговая обработка. То, что таблица истинности оказывается «скомпилированной» в многослойные элементы, не отрицает того факта, что это одношаговый процесс.
Slebetman
2
@ S.Lott: более уместным будет первый комментарий: «логика» аппаратного обеспечения тщательно определена и может быть изучена отдельно от аппаратного обеспечения. И это действительно так. Исследование бинарной логики называется булевой алгеброй.
Slebetman
6

Все это покрыто тщательным терпением в книге «Искусство компьютерного программирования» Дона Кнута.

Эффективные алгоритмы сложения, вычитания, умножения и деления описаны в деталях.

Вы можете читать такие вещи, которые хорошо освещают разделение.

http://research.microsoft.com/pubs/151917/divmodnote.pdf

С. Лотт
источник
5

Это делается в пикосекундах с помощью электронных схем. Google 'аппаратный множитель' и т. Д. Для деталей. Современные процессоры являются чрезвычайно сложным результатом десятилетий непрерывного совершенствования.

Кстати, так как вы не умножаете на повторное сложение, почему вы думаете, что компьютер будет?

Кевин Клайн
источник
Мой вопрос касается причин, лежащих в основе функций, а не самих функций, я понимаю, что это интерпретируется процессором, мне интересно, как. В частности, теория, стоящая за этим и как это может быть воспроизведено в псевдокоде.
Корвин Санто
1
Умножение, которое я делаю в своей голове, - это память. Также длинное умножение требует итерации так, как я это сделал. Я пойду вперёд и соберу функцию для длинного умножения
Корвин Санто
2
@ Корвин, книга, которую я порекомендовал, хорошо послужит тебе, если это тебя интересует. Я также рекомендую «Структура и интерпретация компьютерных программ» Гарольда Абельсона и Джеральда Джея Суссмана. Эти вопросы подробно рассматриваются.
Джонатан Хенсон
Несколько ранних компьютеров поддерживали только сложение и вычитание. Некоторые поддерживают только вычитание! Таким образом, операция x = y * z была реализована так же, как и do (z times) {x + y}, аналогично деление x = y / z было реализовано, как while (y> z) {x + 1; y = y - z}
Джеймс Андерсон
@James: они поддержали смену? Я ожидаю, что умножение было сделано с помощью сдвига и сложения, в то время как деление было сдвигом, сравнением, вычитанием.
Кевин Клайн
4

Это ни в коем случае не является исчерпывающим ответом, но должно дать вам некоторое представление о том, как все реализовано. Как вы, наверное, знаете, числа представлены в двоичном виде. Например, компьютер может представить число 5 как 00000101. Самая базовая операция, которую может сделать компьютер, это сдвиг влево, что даст 00001010, что является десятичным числом 10. Если бы оно было сдвигом вправо дважды, это было бы 00010100 (десятичное число 20). Каждый раз, когда мы сдвигаем цифры влево, 1 раз мы удваиваем число. Скажем, у меня было число x, и я хотел умножить его на 17. Я мог сместить x влево 4 раза, а затем добавить x к результату (16x + x = 17x). Это был бы эффективный способ умножения числа на 17. Это должно дать вам некоторое представление о том, как компьютер может умножать большие числа без простого повторного сложения.

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

WuHoUnited
источник
Просто чтобы прояснить, вы обычно можете переключаться более чем на один бит за раз. Таким образом , эти 4 операции сдвига, на самом деле только одна операция, как: shl r0, 4.
Калеб
4

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

В университете я научился вычислять тригонометрические и логарифмические операции с дюжиной умножений, делений и сложений. Они назвали это серией Тейлора.

До этого мой отец дал мне книгу, в которой эти сложные операции уже были вычислены для сотен значений и представлены в таблицах. Были также некоторые объяснения для оценки ошибки, когда вы хотели получить синус значения между двумя вычисленными значениями.

Целочисленные единицы, единицы с плавающей запятой, GPU и DSP просто реализуют все эти старые методы на кремнии.

mouviciel
источник
3

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

Во-первых, давайте уберем прямой вопрос: как язык программирования эффективно оценивает умножения и сложения. Ответ прост, они собирают их в умножение и добавляют инструкции. Например, следующий код:

a = 1 + 1;
b = a * 20;

просто компилируется в нечто вроде:

ADD 1 1  a
MUL a 20 b

(обратите внимание, что вышеприведенная сборка предназначена для воображаемого процессора, который не существует, для простоты).

В этот момент вы понимаете, что приведенный выше ответ просто сдвигает проблему и решает ее с помощью аппаратной магии. Следующий вопрос, очевидно, состоит в том, как работает эта аппаратная магия?

Давайте сначала посмотрим на более простую проблему: сложение.

Сначала мы делаем знакомую задачу, добавляя в обычную базу 10 чисел:

 17
+28

Первым шагом было бы добавить 7 и 8. Но это приводит к 15, что больше, чем одна цифра. Итак, мы несем 1:

(1)
 17
+28
= 5

Теперь мы добавляем 1, 1 и 2 вместе:

 17
+28
=45

Итак, из этого мы получаем следующие правила:

  1. когда результат сложения больше, чем одна цифра, мы сохраняем младшую значащую цифру и переносим самую значимую цифру вперед

  2. если в нашу колонку перенесена цифра, мы добавим ее вместе с числами, которые добавляем

Теперь пришло время интерпретировать приведенные выше правила в базе 2 - булевой алгебре.

Таким образом, в булевой алгебре сложение 0 и 1 вместе = 1. Добавление 0 и 0 = 0. И добавление 1 и 1 = 10, что является более чем одной цифрой, поэтому мы переносим 1 вперед.

Из этого мы можем построить таблицу истинности:

a b  |  sum  carry
-------------------
0 0  |   0     0
0 1  |   1     0
1 0  |   1     0
1 1  |   0     1

Исходя из этого, мы можем построить две схемы / логические уравнения - одну для вывода суммы и одну для вывода переноса. Самый наивный способ - просто перечислить все входные данные. Любая таблица истинности, независимо от того, насколько большой и сложный может быть пересчитана в этой форме:

(AND inputs in first row) OR (AND of inputs in second row) OR ...

Это в основном сумма продуктов формы. Мы только смотрим на результаты, которые приводят к 1 и игнорируют 0:

sum = (NOT a AND b) OR (a AND NOT b)

Давайте заменим символы И ИЛИ и НЕ символами языка программирования, чтобы их было легче читать:

sum = (!a & b) | (a & !b)

По сути, мы преобразовали таблицу следующим образом:

a b  |  sum  equation
-------------------
0 0  |   0   
0 1  |   1   (!a & b)
1 0  |   1   (a & !b)
1 1  |   0   

Это может быть непосредственно реализовано в виде схемы:

                _____
 a ------------|     |
    \          | AND |-.     ____
     \  ,-NOT--|_____|  \   |    |
      \/                 `--| OR |----- sum
      /\        _____    ,--|____|
     /  `-NOT--|     |  /
    /          | AND |-`
 b ------------|_____|

Наблюдающие читатели в этот момент заметят, что вышеприведенная логика может фактически быть реализована в виде одного шлюза - шлюза XOR, который удобно имеет поведение, требуемое нашей таблицей истинности:

                _____
 a ------------|     |
               | XOR |---- sum
 b ------------|_____|

Но если ваше аппаратное обеспечение не предоставляет вам шлюз XOR, вышеприведенные шаги помогут вам определить и реализовать его в терминах И, ИЛИ и НЕ.

Способ преобразования логических элементов в реальное оборудование зависит от того, какое у вас оборудование. Они могут быть реализованы с использованием различных физических механизмов, если этот механизм обеспечивает своего рода режим переключения. Логические ворота были реализованы со всем: от струй воды или струй воздуха (жидкости) до транзисторов (электроника) и падающих шариков. Это большая тема сама по себе, поэтому я собираюсь просто замять ее и сказать, что логические элементы можно реализовать как физические устройства.

Теперь мы делаем то же самое для сигнала переноса. Поскольку существует только одно условие, когда сигнал переноса истинен, уравнение просто:

carry = a & b

Так что нести несложно:

                _____
 a ------------|     |
               | AND |---- carry
 b ------------|_____|

Объединяя их вместе, мы получим так называемую половину сумматора:

                _____
 a ------;-----|     |
         |     | XOR |---- sum
 b --;---|-----|_____|
     |   |      _____
     |   '-----|     |
     |         | AND |---- carry
     '---------|_____|

Уравнения для приведенной выше схемы, кстати, выглядит так:

sum = a ^ b
carry = a & b

Половина сумматора чего-то не хватает. Мы внедрили первое правило - если результат больше чем одна цифра, чем перенос, но мы не внедрили второе правило - если есть перенос, добавьте его вместе с числами.

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

a b c  |  sum  carry
---------------------
0 0 0  |   0     0
0 0 1  |   1     0
0 1 0  |   1     0
0 1 1  |   0     1
1 0 0  |   1     0
1 0 1  |   0     1
1 1 0  |   0     1
1 1 1  |   1     1

Уравнение для суммы теперь:

sum = (!a & !b & c) | (!a & b & !c) | (a & !b & !c) | (a & b & c)

Мы можем пройти тот же процесс, чтобы вычленить и упростить уравнение и интерпретировать его как схему и т. Д., Как мы делали выше, но я думаю, что этот ответ становится слишком длинным.

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

  1. Разложите проблему, пока вы не сможете работать на уровне одного бита (цифры).

  2. Определите выходы, которые вы хотите, используя таблицу истинности.

  3. Преобразуйте таблицу в логическое уравнение и упростите уравнение.

  4. Интерпретировать уравнение как логические элементы.

  5. Преобразуйте свою логическую схему в реальные аппаратные схемы, реализовав логические элементы.

Вот как на самом деле решаются фундаментальные (или, скорее, низкоуровневые) проблемы - множество таблиц истинности. Настоящая творческая работа состоит в том, чтобы разбить сложную задачу, такую ​​как декодирование MP3 на битовый уровень, чтобы вы могли работать с ней с таблицами истинности.

Извините, у меня нет времени объяснять, как реализовать умножение. Вы можете попытаться разобраться в этом, выяснив правила того, как долго работает умножение, затем интерпретировать его в двоичном формате, а затем попытаться разбить его на таблицы истинности. Или вы можете прочитать Википедию: http://en.wikipedia.org/wiki/Binary_multiplier

slebetman
источник
2

основные арифметические инструкции выполняются с инструкциями по сборке, которые очень эффективны.

Более сложные (или абстрактные) инструкции либо выполняются в сборке с использованием циклических механизмов, либо обрабатываются в стандартных библиотеках.

Изучая математику в колледже, вы начнете изучать такие вещи, как лямбда-исчисление и система обозначений Big-O. Все это и многие другие используются программистами для оценки и создания эффективных алгоритмов. В любом случае, базовые вещи обычно выполняются на низком уровне, например, в сборке или с помощью указателей.

Отличным введением в эту тему является «Кодекс» Чарльза Петцольда.

Джонатан Хенсон
источник
1
Или таблицы поиска. Намного быстрее предварительно вычислить значения и найти их. Примеры Sin / Cos / Tan (целочисленное деление, хотя это предварительно вычисляется и сохраняется в аппаратном обеспечении).
Мартин Йорк,
1

Получите книгу типа « Основы цифровой логики ...» , которая, как мне кажется, все еще довольно стандартна для студентов-первокурсников и второкурсников, изучайте ее (под ред .: это стоит небольшого состояния, поэтому ищите подержанный или предыдущий выпуск Это). Это поможет вам разобраться с сумматорами и множителями и даст вам достаточно знаний, чтобы начать понимать некоторые принципы работы аппаратного обеспечения.

В краткосрочной перспективе ваш ответ станет «потому что он объединяет множество кусочков более простой логики, чтобы вызвать это сложное поведение», а не «потому что это так».

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

jonsca
источник
1

Здесь много хороших ответов. Вы также начали с правильной идеи: сложные операции, такие как умножение, строятся из более простых операций. Как вы уже догадались, существуют более быстрые способы умножения без инструкции умножения, чем использование серии дополнений. Любое умножение может быть реализовано как сумма меньших умножений или как комбинация сдвигов и сложений. Примеры:

a = 5 + 5 + 5 + 5 + 5;           // 5*5, but takes 5 operations
b = (5 << 2) + 5;                // 5*5 in only 2 operations
c = (41 << 4) + (41 << 2) + 41   // 41*21 in 4 operations

Отдел также может быть разбит на более мелкие операции. XOR (^) - это встроенная инструкция для каждого процессора, на которую я когда-либо смотрел, но даже в этом случае она может быть реализована как комбинация AND, OR и NOT.

Однако у меня есть ощущение, что конкретные ответы будут менее удовлетворительными для вас, чем общее представление о видах инструкций, которые процессор предоставляет, и о том, как эти инструкции могут быть объединены в более сложные операции. Нет ничего лучше для такого любопытства, чем здоровая доза ассемблера. Вот очень доступное введение в язык ассемблера MIPS.

Калеб
источник
1

Вот как современный процессор может реализовать умножение двух 64-битных целых чисел:

Вы знаете, как сделать умножение длинной руки. Чтобы умножить два 10-значных чисел, вы должны умножить одно 10-значное число на каждую из 10 цифр другого числа, записать результат из 11 цифр одну под другой и сдвинуть, а затем сложить все числа.

Современный процессор делает это со всеми 64 на 64 бита. Однако умножение двух одноразрядных чисел очень просто: 1 x 1 = 1, все остальные произведения равны нулю. Это реализовано с логическим и. И в отличие от десятичного произведения, где результатом может быть две цифры, двоичное произведение одноразрядных чисел всегда равно одному биту.

Итак, теперь у вас есть 64 строки по 64 бита, которые необходимо добавить. Но 64 дополнения 64-битных чисел являются неоправданными. Таким образом, процессор использует либо сумматор 3/2, либо сумматор 7/3: если вы добавите 3 однобитовых числа, результат может быть 0, 1, 2 или 3, который умещается в два бита. Если вы добавите 7 однобитовых чисел, результатом будет число от 0 до 7, которое может быть представлено 3 битами. IBM утверждает, что они могут сделать сумматор 7/3 всего с 18 примитивными цепями (документация PowerPC), я уверен, что Intel и ARM могут сделать это также.

У вас есть 4096 бит, сгруппируйте их примерно в 600 групп по 7 бит в одинаковых битовых позициях и используйте около 600 7/3 сумматоров, чтобы уменьшить результат с 4096 бит до менее 2000. Затем вы делаете то же самое снова и снова, пока не получите пары битов, которые можно подавать в обычный полный сумматор.

gnasher729
источник