целочисленное вычитание двойной точности с 32-битными регистрами (MIPS)

Я изучаю компьютерную арифметику. В книге, которую я использую (Паттерсон и Хеннесси), перечислены вопросы, приведенные ниже.

Напишите код mips для вычитания целых чисел двойной точности для 64-битных данных. Предположим, что первый операнд находится в регистрах $ t4 (hi) и $ t5 (lo), второй - в $ t6 (hi) и $ t7 (lo).

Мое решение ответа

sub  $t3, $t5, $t7   # Subtract lo parts of operands. t3 = t5 - t7
sltu $t2, $t5, $t7   # If the lo part of the 1st operand is less than the 2nd,
                     #   it means a borrow must be made from the hi part
add  $t6, $t6, $t2   # Simulate the borrow of the msb-of-low from lsb-of-high
sub  $t2, $t4, $t6   # Subtract the hi's. t2 = t4 - t6

Однако автор дал решения этой проблемы, как показано ниже.

Для целых чисел двойной точности со знаком

subu $t3, $t5, $t7   
sltu $t2, $t5, $t7  
add  $t6, $t6, $t2
sub  $t2, $t4, $t6

Для беззнаковых целых чисел двойной точности

subu $t3, $t5, $t7   
sltu $t2, $t5, $t7  
addu $t6, $t6, $t2
subu $t2, $t4, $t6

Насколько я понимаю, разница в работе sub/add и subu/addu заключается в том, что исключение переполнения создается в sub/add, а не в subu/addu. Оба sub/add и subu/addu вычитают / добавляют биты операндов, и интерпретация операндов со знаком или без знака не влияет на результат, в отличие от инструкций slt и sltu.

Вопрос 1
Я делаю вывод из предоставленных автором решений, что обнаружение переполнения обрабатывается, тогда как я не думал об этом в своем решении. Я прав? Есть ли что-нибудь еще, чего мне не хватает?

Вопрос 2
Если мой вывод верен, почему обнаружение переполнения отключено для предоставленных автором решений в случае вычитания беззнаковой двойной точности с помощью addu и subu?


person Abhijith Madhav    schedule 15.10.2010    source источник


Ответы (1)


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

Например, рассмотрим 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-битных слов:

  • дополнение, которое молча игнорирует переполнения (результат усекается)
  • добавление, которое вызывает исключение, когда происходит подписанное переполнение (переполнение возникает, если входные и выходные последовательности интерпретируются как числа со знаком)
  • добавление, которое вызывает исключение, когда происходит переполнение без знака (переполнение возникает, если последовательности intput и output интерпретируются как числа без знака)

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 вызывает исключение.

Как правило, обнаружение переполнения заключается в выполнении действий, которые зависят от интерпретации подписи, которая влияет на обработку самого значимого бита. Для арифметики больших целых чисел все слова, кроме самого значимого, должны рассматриваться как беззнаковые, поэтому везде _12 _ / _ 13_. В качестве первого шага все станет легче понять, если вы сначала сконцентрируетесь на беззнаковой арифметике без исключения (тогда вы просто используете addu и subu и никогда add или sub).

person Thomas Pornin    schedule 27.10.2010
comment
Большое спасибо за подробный ответ. Вы говорите: «Для беззнаковой арифметики не требуется никаких исключений». Есть ли особая причина, по которой не ожидается возникновения исключения во время беззнаковой арифметики? - person Abhijith Madhav; 28.10.2010
comment
В C беззнаковая арифметика подразумевает тихое усечение: это то, что говорит стандарт. Один может иметь беззнаковые 64-битные числа с исключениями при переполнении, но для 64-битных unsigned long long нужно тихое усечение. Паттерсон и Хеннесси также хотят имитировать то, что MIPS предоставляет для 32-битных данных, то есть беззнаковую арифметику без исключения и арифметику со знаком с исключениями при переполнении. - person Thomas Pornin; 28.10.2010
comment
Ох, хорошо. Теперь я понимаю всю картину. Еще раз спасибо за подробный ответ. Я очень ценю это. - person Abhijith Madhav; 01.11.2010
comment
Кстати, похоже, что stackoverflow.com не отправляет письмо, когда кто-то опубликовал новые комментарии, даже если он подписался на вопрос. Все эти дни я ждал, думая, что вы не успели ответить на мой вопрос. - person Abhijith Madhav; 01.11.2010