Я не могу понять, почему микропроцессорные системы используют числа без знака. Я полагаю, что стоимость просто вдвое превышает число условных веток, так как для алгоритма, отличного от .etc, нужен алгоритм, отличный от подписанного, но есть ли еще алгоритмы, для которых числа без знака являются существенным преимуществом?
Отчасти мой вопрос: почему они должны быть в наборе команд, а не поддерживаться компилятором?
Ответы:
Числа без знака являются одной интерпретацией последовательности битов. Это также самая простая и наиболее используемая интерпретация внутри процессора, потому что адреса и коды операций - это просто биты. Адресация памяти / стека и арифметика - основа микропроцессора, ну и обработки. Двигаясь вверх по пирамиде абстракции, другая частая интерпретация битов - это символ (ASCII, Unicode, EBCDIC). Затем существуют другие интерпретации, такие как IEEE с плавающей точкой, RGBA для графики и так далее. Ни одно из них не является простым числом со знаком (IEEE FP не является простым, и арифметическое использование их очень сложно).
Кроме того, с арифметикой без знака довольно просто (если не наиболее эффективно) реализовать другие. Обратное неверно.
источник
Основную часть стоимости оборудования для операций сравнения составляет вычитание. Вывод вычитания, используемого для сравнения, по существу состоит из трех битов состояния:
При правильной комбинации тестирования этих трех битов после операции вычитания мы можем определить все подписанные реляционные операции, а также все беззнаковые реляционные операции (эти биты также определяют, как обнаруживается переполнение, подписано и не подписано). Одно и то же базовое аппаратное обеспечение ALU может использоваться совместно для реализации всех этих сравнений (не говоря уже о команде вычитания), вплоть до окончательной проверки этих трех битов состояния, которые отличаются в соответствии с желаемым сравнительным сравнением. Таким образом, это не так много дополнительного оборудования.
Единственная реальная стоимость - это необходимость кодирования дополнительных режимов сравнения в архитектуре набора команд, что может незначительно снизить плотность команд. Тем не менее, вполне нормально, что на оборудовании есть много инструкций, которые не используются ни одним из языков.
источник
Потому что, если вам нужно считать что-то, что всегда
>= 0
, вы бы без необходимости сократили свое счетное пространство пополам, используя целые числа со знаком.Подумайте об автоинкрементном INT PK, который вы можете поместить в таблицы базы данных. Если вы используете там целое число со знаком, ваша таблица хранит в HALF столько записей, сколько она могла бы для того же размера поля без НИКАКОЙ выгоды.
Или октеты цвета RGBa. Мы не хотим неловко начинать считать эту концепцию натурального числа с отрицательным числом. Число с подписью либо сломает ментальную модель, либо уменьшит вдвое пространство. Целое число без знака не только соответствует концепции, но и обеспечивает двойное разрешение.
С аппаратной точки зрения целые числа без знака просты. Они, вероятно, самая простая структура битов для выполнения математики. И, без сомнения, мы могли бы упростить аппаратное обеспечение, моделируя целочисленные типы (или даже числа с плавающей запятой!) В компиляторе. Итак, почему целые числа без знака и со знаком реализованы в аппаратном обеспечении?
Ну что ж ... производительность!
Реализовать целые числа со знаком в аппаратном обеспечении более эффективно, чем в программном обеспечении. Аппаратные средства могут быть проинструктированы выполнять математику для любого типа целого числа в одной инструкции. И это очень хорошо , потому что аппаратные средства соединяют куски более или менее параллельно. Если вы попытаетесь симулировать это в программном обеспечении, целочисленный тип, который вы выберете для «симуляции», несомненно, потребует много инструкций и будет заметно медленнее.
источник
Ваш вопрос состоит из двух частей:
Какова цель целых чисел без знака?
Стоят ли целые числа без знака?
1. Какова цель целых чисел без знака?
Простые числа без знака представляют класс величин, для которых отрицательные значения не имеют смысла. Конечно, вы можете сказать, что ответ на вопрос "сколько у меня яблок?" может быть отрицательным числом, если вы кому-то должны несколько яблок, но как насчет вопроса "сколько у меня памяти?" - у вас не может быть отрицательного количества памяти. Таким образом, целые числа без знака очень подходят для представления таких величин, и они имеют преимущество в том, что могут представлять в два раза больший диапазон положительных значений, чем целые числа со знаком. Например, максимальное значение, которое вы можете представить с помощью 16-разрядного целого числа со знаком, равно 32767, а с 16-разрядным целым числом без знака - 65535.
2. Стоят ли целые числа без знака?
Целые числа без знака на самом деле не представляют никаких проблем, так что, да, они того стоят. Видите ли, они не требуют дополнительного набора «алгоритмов»; схема, необходимая для их реализации, является подмножеством схемы, необходимой для реализации целых чисел со знаком.
CPU не имеет одного множителя для целых чисел со знаком и другого множителя для целых чисел без знака; у него есть только один множитель, который работает немного по-разному в зависимости от характера операции. Для поддержки знакового умножения требуется чуть больше схем, чем без знака, но, поскольку его нужно поддерживать в любом случае, умножение без знака предоставляется практически бесплатно, оно включено в пакет.
Что касается сложения и вычитания, то в схемотехнике вообще нет никакой разницы. Если вы прочитаете о так называемом представлении целых чисел в виде дополнения к двум, то обнаружите, что оно разработано настолько умно, что эти операции могут выполняться точно так же, независимо от природы целых чисел.
Сравнение также работает таким же образом, поскольку это не что иное, как вычитание и отбрасывание результата, единственное различие заключается в инструкциях условного перехода (перехода), которые работают, просматривая различные флаги ЦП, которые устанавливаются предшествующая (сравнительная) инструкция. В этом ответе: /programming//a/9617990/773113 вы можете найти объяснение того, как они работают на архитектуре Intel x86. Что происходит, так это то, что обозначение инструкции условного перехода как подписанной или неподписанной зависит от того, какие флаги она проверяет.
источник
Микропроцессоры изначально без знака. Числа со знаком - это то, что реализовано, а не наоборот.
Компьютеры могут работать и работают без подписанных чисел, но нам, людям, нужны отрицательные числа, поэтому была изобретена подпись.
источник
Потому что у них есть еще один бит, который легко доступен для хранения, и вам не нужно беспокоиться об отрицательных числах. Там не намного больше, чем это.
Теперь, если вам нужен пример того, где вам понадобится этот дополнительный бит, есть много чего найти, если вы посмотрите.
Мой любимый пример - битборды в шахматных движках. На шахматной доске 64 квадрата, что
unsigned long
обеспечивает идеальное хранилище для различных алгоритмов, вращающихся вокруг генерации ходов. Учитывая тот факт, что вам нужны бинарные операции (а также операции сдвига !!), легко понять, почему легче не беспокоиться о том, что происходит, если установлен MSB. Это можно сделать с подписанным длинным, но намного проще использовать без подписанного.источник
Имея чистый математический фон, это немного более математический подход для всех, кто интересуется.
Если мы начнем с 8-битного целого числа со знаком и без знака, то мы получим в основном целые числа по модулю 256, что касается сложения и умножения, при условии, что дополнение 2 используется для представления отрицательных целых чисел (и именно так это делает каждый современный процессор) ,
Где вещи различаются в двух местах: одно это операции сравнения. В некотором смысле целые числа по модулю 256 лучше всего рассматривать как круг чисел (как целые по модулю 12 на старомодном аналоговом циферблате). Чтобы сделать числовые сравнения (это x <y) значимыми, нам нужно было решить, какие числа меньше других. С точки зрения математика, мы хотим как-то встроить целые числа по модулю 256 в набор всех целых чисел. Преобразование 8-битного целого числа, двоичное представление которого представляет собой все нули, в целое число 0 - очевидная вещь, которую нужно сделать. Затем мы можем перейти к отображению других так, чтобы «0 + 1» (результат обнуления регистра, скажем, ax и увеличения его на единицу с помощью «inc ax») переходил к целому числу 1 и так далее. Мы можем сделать то же самое с -1, например, сопоставив «0-1» с целым числом -1 и «0-1-1» в целое число -2. Мы должны убедиться, что это вложение является функцией, поэтому не может отобразить одно 8-битное целое число на два целых числа. Таким образом, это означает, что если мы отобразим все числа в набор целых чисел, то будет 0, наряду с некоторыми целыми числами меньше 0 и некоторыми больше 0. Существуют по существу 255 способов сделать это с 8-битным целым числом (согласно до какого минимума вы хотите, от 0 до -255). Затем вы можете определить «x <y» в терминах «0 <y - x».
Существует два распространенных варианта использования, для которых целесообразна аппаратная поддержка: один со всеми ненулевыми целыми числами, превышающими 0, и один с примерно 50/50, разделенными на 0. Все остальные возможности легко эмулируются путем перевода чисел с помощью дополнительного «add». и sub 'before операции, и необходимость в этом настолько редка, что я не могу вспомнить явный пример в современном программном обеспечении (поскольку вы можете просто работать с более крупной мантиссой, скажем, 16 битами).
Другая проблема заключается в отображении 8-битного целого числа в пространство 16-битных целых. -1 идет к -1? Это то, что вы хотите, если 0xFF означает -1. В этом случае разумным является расширение знака, поэтому 0xFF переходит к 0xFFFF. С другой стороны, если 0xFF должен был представлять 255, то вы хотите, чтобы он был сопоставлен с 255, следовательно, с 0x00FF, а не с 0xFFFF.
В этом и разница между операциями «сдвиг» и «арифметический сдвиг».
В конечном счете, однако, все сводится к тому, что целые числа в программном обеспечении - это не целые числа, а представления в двоичном виде, и только некоторые из них могут быть представлены. При проектировании аппаратного обеспечения необходимо сделать выбор в отношении того, что делать в аппаратном обеспечении. Поскольку с дополнением 2 операции сложения и умножения идентичны, имеет смысл представлять отрицательные целые числа таким образом. Тогда это только вопрос операций, которые зависят от того, какие целые числа должны представлять ваши двоичные представления.
источник
Давайте рассмотрим стоимость реализации для добавления целых чисел без знака в структуру ЦП с существующими целыми числами со знаком.
Типичный процессор нуждается в следующих арифметических инструкциях:
Также нужны логические инструкции:
Чтобы выполнить вышеуказанные ветви для целочисленных сравнений со знаком, проще всего заставить команду SUB установить следующие флаги:
Затем арифметические ветви реализуются следующим образом:
Отрицания этого должны очевидно следовать из того, как они реализованы.
Таким образом, ваш существующий дизайн уже реализует все это для целых чисел со знаком. Теперь давайте рассмотрим, что нам нужно сделать, чтобы добавить целые числа без знака:
Обратите внимание, что в каждом случае модификации очень просты и могут быть реализованы просто путем включения или выключения небольшого участка схемы или путем добавления нового регистра флага, который может управляться значением, которое должно быть вычислено как часть реализация инструкции в любом случае.
Поэтому стоимость добавления неподписанных инструкций очень мала . Что касается того, почему это должно быть сделано , обратите внимание, что адреса памяти (и смещения в массивах) по своей природе являются значениями без знака. Поскольку программы тратят много времени на обработку адресов памяти, наличие типа, который обрабатывает их правильно, облегчает написание программ.
источник
Числа без знака существуют в основном для обработки ситуаций, когда нужно обернуть алгебраическое кольцо (для 16-разрядного типа без знака это будет кольцо конгруэнтов целых чисел 65536). Возьмите значение, добавьте любую величину меньше модуля, и разница между двумя значениями будет суммой, которая была добавлена. В качестве примера из реальной жизни, если счетчик коммунальных услуг в начале месяца читает 9995, а один использует 23 единицы, счетчик в конце месяца будет читать 0018. При использовании типа алгебраического кольца нет необходимости делать что-то особенное, чтобы справиться с переполнением. Вычитание 9995 из 0018 даст 0023, точно количество единиц, которые были использованы.
На PDP-11, машине, для которой C был впервые реализован, не было целочисленных типов без знака, но знаковые типы можно было бы использовать для модульной арифметики, которая заключена между 32767 и -32768, а не между 65535 и 0. Целочисленные инструкции в некоторых других платформы, однако, не оборачивали вещи чисто; вместо того, чтобы требовать, чтобы реализации должны были эмулировать целые числа с двумя дополнительными компонентами, используемые в PDP-11, язык вместо этого добавил типы без знака, которые в основном должны были вести себя как алгебраические кольца, и позволял типам со знаком целыми числами вести себя другими способами в случае переполнения.
В первые дни C было много количеств, которые могли превышать 32767 (общий INT_MAX), но не 65535 (общий UINT_MAX). Таким образом, стало распространенным использование беззнаковых типов для хранения таких количеств (например, size_t). К сожалению, в языке нет ничего, чтобы различать типы, которые должны вести себя как числа с дополнительным битом положительного диапазона, и типы, которые должны вести себя как алгебраические кольца. Вместо этого язык заставляет типы меньше «int» вести себя как числа, тогда как полноразмерные типы ведут себя как алгебраические кольца. Следовательно, вызывая функцию как:
с (65535, 65535) будет иметь одно определенное поведение в системах с
int
16 битами (т. е. с возвратом 1), другое поведение сint
33 битами или более (с возвратом 0xFFFE0001) и неопределенное поведение в системах, где «int» находится где угодно в между [обратите внимание, что gcc обычно будет давать арифметически правильные результаты с результатами между INT_MAX + 1u и UINT_MAX, но иногда генерирует код для вышеуказанной функции, который завершается с такими значениями!]. Не очень полезно.Тем не менее, отсутствие типов, которые ведут себя последовательно как числа или последовательно как алгебраическое кольцо, не меняет того факта, что типы алгебраических колец практически необходимы для некоторых видов программирования.
источник