imull операция по сборке ia32

Я хочу сделать незаметную операцию в сборке и вернуть результат в C.

Сигнатура моей функции 'long long multiplicar(void)' и код:

multiplicar:
    movl op1, %eax
    imull op2, %eax
    adcl $0, %edx

    ret

Мой op2 - 3. Когда мой op1 -399, работает хорошо (дает 1197). Но когда мой op1 -399, я получаю 4294966093 и не знаю почему. Я должен использовать CDC?

Мои op1 и op2 - длинные длинные типы. Спасибо

1 ответ

Решение

imul Инструкция, если ей даны 32-битные операнды, выполняет 32-битное умножение со знаком. Это дает результат до 64 битов, однако в формах с двумя / тремя операндами сохраняется только наименее значимое слово с переполнением, указанным через перенос.

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

В этом случае после последнего редактирования кажется, что цель состоит в том, чтобы умножить две 64-битные переменные вместе и получить усеченный 64-битный результат. Достижение этого с помощью 32x32=>64-битного примитива требует связывания воедино четырех умножений, что равнозначно методу начальной школы. То есть (a<<32|b) * (c<<32|d) = (a*c<<64) + (a*d<<32) + (b*c<<32) + (b*d<<0), a*c Однако здесь можно отбросить термин, поскольку нам требуются только младшие 64-битные значения результата.

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

К счастью, процессор фактически поддерживает 64-битное умножение изначально, если мы вместо этого используем модуль с плавающей запятой 8087. Обратите внимание, что во избежание ошибок округления управляющее слово с плавающей точкой должно быть установлено с полной 64-битной точностью (_controlfp(_PC_64,_MCW_PC)), в отличие от 53 битов, которые обычно используются.

multiply: ;int64_t __cdecl multiply(int64_t lhs, int64_t rhs)
        fildq 4(%esp)
        fildq 12(%esp)
        fmul
        fistpq 4(%esp)
        movl 4(%esp),%eax
        movl 8(%esp),%edx
        ret

Тем не менее, обратите внимание, что переполнения, требующие полной 128-битной точности, не дают правильного усеченного 64-битного результата, и вопрос о том, не является ли переполнение, должен быть обработан.

Другие вопросы по тегам