Отрицательное целое число ноль - PullRequest
1 голос
/ 05 июня 2019

Почему Python может представлять отрицательный ноль в float, но не может представлять отрицательный ноль в int?

Более конкретно:

a = 0.0
print(a)
# 0.0

b = -a
print(b)
# -0.0

НО:

a = 0
print(a)
# 0

b = -a
print(b)
# 0

(мне известно об обсуждении здесь отрицательный ноль в python о отрицательном плавающем нуле, но int там на самом деле не обсуждаются).

1 Ответ

4 голосов
/ 05 июня 2019

Исторически существовали целочисленные форматы, которые могли представлять как -0, так и +0. И знак и величина и одно дополнение может представлять -0 и +0. Они оказались менее полезными, чем дополнение к двум , которое завоевало благосклонность и сегодня повсеместно.

Дополнение Two имеет некоторые числовые свойства, которые делают его немного приятнее для реализации на аппаратном уровне, а наличие двух нулей создает определенные неудобства для программистов. (Я слышал об ошибках, таких как остаток на счете -0 вместо +0, в результате чего человеку отправляли счет, когда его не следовало.)

Плавающая точка использует знак и величину, поэтому она может представлять как −0, так и +0. Из-за природы числа с плавающей запятой арифметические свойства дополнения до двух не очень помогут реализации с плавающей запятой, а наличие двух нулей позволяет программисту использовать немного дополнительной информации в некоторых случаях.

Таким образом, выбор целочисленных форматов и форматов с плавающей точкой мотивируется полезностью, а не математической необходимостью.

Взгляд на целочисленную арифметику

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

Далее мы хотим обработать отрицательные числа. При написании мы обрабатываем отрицательные числа, помещая знак спереди, поэтому нашей первой мыслью может быть сделать то же самое с битами: использовать бит впереди для обозначения отрицательного числа. Теперь у нас есть представление знака и величины. 0001 представляет +1, а 1001 представляет -1. 0010 представляет +2, а 1010 представляет -2. 0111 представляет +7, а 1111 представляет -7. И, конечно, 0000 представляет +0, а 1000 представляет -0. Это идея, и тогда мы должны ее реализовать. У нас уже есть сумматор, и, если мы заполним его 0010 (2) и 0011 (3), он правильно выведет 0101 (5). Но если мы подадим его 0011 (3) и 1001 (-1), он выдаст 1100 (−4). Таким образом, мы должны изменить это. Что ж, это не так уж и плохо, у нас есть единица вычитания для беззнакового двоичного файла, поэтому мы можем посмотреть на первый бит, и, если мы добавляем отрицательное число, мы вычитаем вместо добавления. Это работает для некоторых операций; для 0011 и 1001, наблюдение за первым 1 на втором операнде и подача 011 и 001 в блок вычитания приведет к 010 (2), что правильно. Но, если у нас есть 0010 и 1011, подача 010 и 011 к блоку вычитания может привести к некоторой индикации ошибки (изначально она была предназначена для двоичного файла без знака) или может «обернуть» и произвести 111 (потому что такое обертывание, наряду с «заемным») out »в выводе, заставляет блок вычитания работать как часть дизайна для вычитания более широких чисел). В любом случае, это неправильно для наших подписанных номеров; мы хотим, чтобы выходные данные 0010 (2) плюс 1011 (-3) были 1001 (-1). Поэтому мы должны разработать новые арифметические единицы, которые справятся с этим. Возможно, при добавлении чисел смешанных знаков они выясняют, какой из них больше по величине, вычитают меньший из большего и затем применяют знаковый бит большего. В любом случае, у нас есть достаточно много работы, чтобы спроектировать сложение и вычитание.

Еще одно предложение, чтобы сделать номеротрицательно, инвертируйте каждый бит.Это называется дополнением.Это легко понять и подходит под понятие отрицания - просто отрицай все.Давайте рассмотрим, как это влияет на наши арифметические единицы.Для комбинаций +3 или −3 с +2 или −2 мы бы хотели получить следующие результаты: 0011 (3) + 0010 (2) = 0101 (5), 0011 (3) + 1101 (−2) = 0001 (1), 1100 (-3) + 0010 (2) = 1110 (-1) и 1100 (-3) + 1101 (-2) = 1010 (-5).После проверки есть простой способ адаптировать наш двоичный сумматор, чтобы он работал: выполните сложение для всех четырех битов, как если бы они были двоичными числами без знака, и, если есть перенос старшего бита, добавьте его обратно внизкий битВ двоичном без знака 0011 + 0010 = 0101 без переноса итоговый результат равен 0101. 0011 + 1101 = 0000 с переносом, поэтому конечный результат равен 0001. 1100 + 0010 = 1110 без переноса, поэтому конечный результат равен 11101100 + 1101 = 1001 с переносом, итоговый результат - 1010.

Это хорошо;сумматор нашего комплемента проще, чем сумматор знака и величины.Ему не нужно сравнивать величины и не нужно делать вычитание для обработки отрицательных чисел.Мы можем сделать это дешевле и получить больше прибыли.

Тогда кто-то придумает идею дополнения до двух.Вместо инвертирования каждого бита мы концептуально вычтем число из 2 n , где n - количество бит.Таким образом, 10000 - 0001 = 1111 представляет -1, а 1110 - -2, 1101 - 3 и так далее.Что это делает с нашим сумматором?

В двоичном формате без знака: 0010 (2) + 1101 (13) = 1111 (15).В дополнение к двум, 0010 (2) + 1101 (−3) = 1111 (−1).Биты одинаковы!Это на самом деле работает для всех двух дополнительных чисел;добавление битовых комбинаций для чисел без знака дает те же результаты, которые мы хотим получить для добавления чисел дополнения до двух. Мы можем использовать точно такие же логические элементы для двоичного знака без знака и дополнения до двух. Это замечательно, дать этому сотруднику повышение.Это то, что делает современное оборудование;для сложения или вычитания чисел дополнения двух используются те же арифметические единицы, что и для сложения или вычитания чисел без знака.

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

(Существует разница между добавлением без знака и добавлением двух дополнений: как обнаруживается переполнение. В добавлении без знака происходит переполнение, если имеетсявынос старшего бита. В добавлении к двум дополнениям происходит переполнение, если есть перенос старших битов амплитуды, следовательно, перенос в знак. Сумматорные блоки обычно обрабатывают это, сообщая обоим признакам в одной формеили другое. Эта информация, при желании, проверяется в последующих инструкциях; она не влияет на само дополнение.)

...