Ответ 1
ПРЕДУПРЕЖДЕНИЕ Этот ответ длинный!
... и он заполнен ненужными объяснениями - но я всегда хотел написать что-то более продолжительное относительно умножения.
Немного теории
При умножении двух чисел a и b длины n результат имеет длину 2 n † и, самое главное, k-я цифра зависит только от младших k цифр (дается доказательство в Приложении A).
x86 imul
две формы
Инструкция умножения x86 imul
представлена в двух формах: полной форме и частичной форме.
Первая форма имеет вид n × n → 2 n, что означает, что она дает результат в два раза больше размера операндов - мы знаем из теории, почему это имеет смысл. Например
imul ax ;16x16->32, Result is dx:ax
imul rax ;64x64->128, Result is rdx:rax
Вторая форма имеет вид n × n → n, это обязательно вырезает некоторую информацию.
В частности, эта форма принимает только младшие n бит результата.
imul ax, ax ;16x16->16, Lower WORD of the result is ax
imul rax, rax ;64x64->64, Lower QWORD of the result is rax
Только одна версия операнда имеет первую форму.
Две инструкции: imul
vs mul
Независимо от используемой формы, процессор всегда вычисляет результат с размером в два раза больше операндов (т.е. как первая форма).
Чтобы иметь возможность сделать это, операнды сначала преобразуются из их размера n в размер 2 n (например, от 64 до 128 бит).
Дополнительную информацию см. В Приложении B.
Умножение выполняется, и полный или частичный результат сохраняется в месте назначения.
Разница между imul
и mul
заключается в том, как преобразуются операнды.
Поскольку размер расширен, этот тип преобразования называется extension.
Инструкция mul
просто заполняет верхнюю часть нулями - она равна нулю.
Команда imul
реплицирует бит высокого порядка (первый слева) - это называется расширением знака и имеет интересное свойство преобразования двух дополнений подписали количество n бит в число со знаком 2 n битов с одинаковыми знаками и модулем (т.е. он делает то же самое, читателю остается найти встречный пример для случая с нулевым расширением),
How mul extends How imul extends
and operand and operand
+----+ +----+ +----+ +----+
|0...| |1...| |0...| |1...|
+----+ +----+ +----+ +----+
+----+----+ +----+----+ +----+----+ +----+----+
|0000|0...| |0000|1...| |0000|0...| |1111|1...|
+----+----+ +----+----+ +----+----+ +----+----+
Тезис
Разница между imul
и mul
заметна только от (n + 1) -го бита вперед.
Для 32-разрядного операнда это означает, что только верхняя 32-битная часть полного результата в конечном итоге будет отличаться.
Это легко увидеть, поскольку младшие n бит одинаковы для обеих команд, и, как мы знаем из теории, первые n бит результата зависят только от первых n бит операндов.
Таким образом, тезис: результат частичной формы imul
идентичен результату mul
.
Затем почему imul
выходит?
Поскольку он более гибкий - он имеет два или три операнда, а mul
имеет очень древний интерфейс.
Поскольку он устанавливает флаги в соответствии с подписанным умножением - CF и OF устанавливаются, если частичный результат отбрасывает какую-либо значимую информацию (техническое условие: расширение знака частичного результата отличается от полного результата), например, в случае переполнения.
Именно поэтому две и три формы операндов не называются mul
, что в противном случае было бы идеально подходящим именем.
Практика
Чтобы проверить все это на практике, мы можем запросить компилятор [ Комментарий от BeeOnRope
Кроме того, расширение знака, вероятно, также концептуально
Аналогично расширение знака может происходить "концептуально", но, вероятно, не в аппаратном обеспечении. У них не будет дополнительных проводов и транзисторов только для того, чтобы сделать знак или нулевое расширение, что добавило бы много большого количества в уже огромный множитель, но будет использовать некоторые другие трюки, чтобы выполнить умножение "как бы", которое произошло.
† Двоичные числа длины n находятся в порядке величины 2 n поэтому умножение двух таких чисел имеет порядок 2 n · 2 n= 2 n + n= 2 2 n. Как и длина длины 2 n.