Для сложения и вычитания нет разницы между знаковыми и неподписанными операндами, за исключением понятия переполнения. Переполнение - это то, что происходит, когда числовое значение результата не соответствует интерпретации полученной вами последовательности битов.
Например, рассмотрим 8-битные последовательности (MIPS имеет 32-битные регистры, но для моих примеров 8-битные проще). Предположим, что беззнаковая интерпретация: 8-битная последовательность представляет числовое значение от 0 до 255 (включительно). Если я добавлю 10010011 (числовое значение 147) к 01110110 (числовое значение 118), я получу 00001001 (числовое значение 9). 9 не равно 147 + 118. Я получаю этот результат, потому что математическое значение равно 265, что не может вместиться в 8 бит. Результат сложения потребовал бы 9 битов, но верхний девятый бит был отброшен.
Теперь представьте тот же пример с подписанным толкованием. 10010011 теперь имеет числовое значение -109. 01110110 по-прежнему имеет числовое значение 118, а полученный результат (00001001) имеет значение 9. Математическая сумма -109 и 118 равна 9, поэтому переполнения нет.
Это означает, что понятие переполнения зависит от того, как вы интерпретируете значения. Механика сложения одинакова для интерпретаций со знаком и без знака (для одних и тех же входных последовательностей битов вы получаете одинаковую последовательность выходных битов - в этом весь смысл использования дополнения до двух для отрицательных значений со знаком), но обработка переполнения отличается.
Архитектура MIPS предоставляет средства для запуска исключений при переполнении. Концептуально, существует три возможных операций сложения над 32-битными словами:
- дополнение, которое молча игнорирует переполнения (результат усекается)
- добавление, которое вызывает исключение при переполнении со знаком (переполнение происходит, если входная и выходная последовательности интерпретируются как числа со знаком)
- добавление, которое вызывает исключение при переполнении без знака (переполнение, если входная и выходная последовательности интерпретируются как числа без знака)
MIPS реализует первые два вида дополнений, соответственно с кодами операций addu
и add
. В документации MIPS они называются соответственно без знака и со знаком арифметики . Не существует кода операции для вызова исключений при переполнении без знака. На практике компиляторы C используют только addu
, но они могут использовать add
для подписанных типов (это разрешено стандартом C, но может привести к поломке огромного количества существующего кода). Компиляторы Ada используют add
, потому что Ada делает проверку переполнения обязательной.
Как говорится ...
Паттерсон и Хеннесси хотят реализовать арифметику со знаком и без знака на 64-битных целых числах. Для арифметики без знака они не хотят никаких исключений, поэтому они используют addu
и subu
. Для арифметики со знаком они хотят, чтобы произошло исключение, когда математический результат не уместился бы в 64-битную последовательность со знаковой интерпретацией. Они не хотят вызывать исключение из-за некоторого ложного переполнения при обработке младших 32-битных половин. Вот почему они используют subu
для низких частей.
Ваше решение неверно, потому что оно может вызвать исключение, если оно не должно. Предположим, что вы хотите вычесть 2000000000 (два миллиарда) из -2000000000 (минус два миллиарда). Математический результат - 4000000000 (четыре миллиарда). Два операнда и результат определенно помещаются в 64 бита (представляемый диапазон от -9223372036854775808 до 9223372036854775807). Следовательно, для 64-битной знаковой арифметики переполнения нет: не должно быть исключений. Однако в этой ситуации ваш первый sub
сообщит о переполнении. Это sub
работает с 32-битными значениями и 32-битной арифметикой со знаком. Его операнды будут 01110111001101011001010000000000 и 10001000110010100110110000000000. Обратите внимание, что оба эти значения умещаются в 32 бита: 32-разрядная интерпретация этих значений со знаком соответственно равна плюс и минус два миллиарда. Результат вычитания, однако, составляет четыре миллиарда, и он не умещается в 32 бита (как число со знаком). Таким образом, ваш sub
вызывает исключение.
Как правило, обнаружение переполнения связано с выполнением действий, которые зависят от интерпретации подписи, что влияет на обработку наиболее значимого бита. Для большой целочисленной арифметики все слова, кроме самого значимого, должны рассматриваться как беззнаковые, следовательно, addu
/ subu
везде. В качестве первого шага все будет проще понять, если вы сначала сконцентрируетесь на беззнаковой арифметике, без исключения (тогда вы просто используете addu
и subu
и никогда add
или sub
) .