Поведение отрицательного нуля на комплементарной архитектуре?

Рассмотрим следующий код на архитектуре дополнения:

int zero = 0;
int negzero = -0;
std::cout<<(negzero < zero)<<std::endl;
std::cout<<(negzero <= zero)<<std::endl;
std::cout<<(negzero == zero)<<std::endl;
std::cout<<(~negzero)<<(~zero)<<std::endl;
std::cout<<(1 << negzero)<<std::endl;
std::cout<<(1 >> negzero)<<std::endl;
  • Какой вывод даст код?
  • Какие строки определены стандартом, какие строки зависят от реализации и какие строки имеют неопределенное поведение?

9

Решение

Основываясь на моей интерпретации стандарта:

Стандарт C ++ в §3.9.1 / p3 Фундаментальные типы [basic.fundamental] на самом деле бросает мяч в стандарте C:

Целочисленные типы со знаком и без знака должны удовлетворять ограничениям
приведены в стандарте C, раздел 5.2.4.2.1.

Теперь, если мы перейдем к разделу 5.2.4.2.1 ИСО / МЭК 9899: 2011, в качестве прямой ссылки §6.2.6.2 / p2 Целочисленные типы (Акцент на шахте):

Если бит знака равен нулю, он не должен влиять на результирующее значение. Если
бит знака равен единице, значение должно быть изменено в одном из
следующие способы:

  • соответствующее значение со знаком бит 0 отменяется (знак и
    величина);

  • знаковый бит имеет значение — (2 ^ M) (дополнение до двух);

  • знаковый бит имеет значение — (2 ^ M — 1) (дополнение).

Что из этого применимо, определяется реализацией, является ли значение со знаком бит 1 и всеми битами значения ноль (для первых двух),
или со знаком бит и все биты значения 1 (для дополнения), является
представление ловушки или нормальное значение. В случае знака и
величина и дополнения, если это представление является нормальным
Значение это называется отрицательным нулем.

Следовательно, наличие отрицательного нуля определяется реализацией.

Если мы продолжим в пункте 3:

Если реализация поддерживает отрицательные нули, они должны быть сгенерированы
только:

  • &, |, ^, ~, <<и >> операторы с операндами, которые производят такие
    ценность;

  • операторы +, -, *, / и%, где один операнд является отрицательным нулем
    и результат равен нулю;

  • составные операторы присваивания на основе вышеуказанных случаев.

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

Следовательно, не определено, будут ли отображаемые вами связанные случаи вообще генерировать отрицательный ноль.

Теперь, исходя из пункта 4:

Если реализация не поддерживает отрицательные нули, поведение
&, |, ^, ~, <<и >> операторы с операндами, которые будут производить
такое значение не определено.

Следовательно, приведет ли связанная операция к неопределенному поведению, зависит от того, поддерживает ли реализация отрицательные нули.

3

Другие решения

Прежде всего, ваша первая предпосылка неверна:

int negzero = -0;

должен производить нормальный ноль на любой совместимой архитектуре.

Ссылки на это были даны в ответе 101010:

3.9.1 Фундаментальные типы [basic.fundamental] §3:

… Целое число со знаком и без знака
типы должны удовлетворять ограничениям, указанным в стандарте C, раздел 5.2.4.2.1.

Позже в C ссылка:
5.2.4.2.1 Размеры целочисленных типов

… Прямые ссылки: представления типов (6.2.6)

и (все еще С):
6.2.6 Представления типов / 6.2.6.2 Целочисленные типы § 3

Если реализация поддерживает отрицательные нули, они должны быть сгенерированы только:

  • &, |, ^, ~, <<и >> операторы с аргументами, которые производят такое значение;

  • операторы +, -, *, / и%, где один аргумент — отрицательный ноль, а результат —
    нуль;

  • составные операторы присваивания на основе вышеуказанных случаев.

Так negzero = -0 является не такая конструкция и не должны произвести отрицательный 0.

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

Стандарт C ++ вообще не говорит об отрицательных нулях, а стандарт C просто говорит о них, что их существование зависит от реализации. Я не смог найти ни одного абзаца, явно говорящего, должен ли отрицательный ноль быть равным нормальному нолю или нет для оператора реляции или равенства.

Поэтому я просто приведу ссылку на C: 6.5.8 Реляционные операторы §6

Каждый из операторов < (меньше чем),> (больше чем), <= (меньше или равно) и> =
(больше или равно) должно давать 1, если указанное отношение истинно, и 0, если оно ложно.92)
Результат имеет тип int.

и в C ++ 5.9 Реляционные операторы [expr.rel] §5

Если оба операнда (после преобразования) имеют арифметический или перечислимый тип, каждый из операторов должен дать
Значение true, если указанное отношение истинно, и false, если оно ложно.

Моя интерпретация стандарта заключается в том, что реализация может разрешить альтернативное представление целочисленного значения 0 (отрицательный ноль) но это все еще представление значения 0, и оно должно работать соответствующим образом в любом арифметическом выражении, потому что C 6.2.6.2 целочисленных типов § 3 говорит:

отрицательные нули […] генерируются только […] операторы +, -, *, / и%, где один аргумент — отрицательный ноль, а результат —
нуль

Это означает, что если результат не равен 0, отрицательный 0 должен работать как нормальный ноль.

Таким образом, эти две линии, по крайней мере, идеально определены и должны производить 1:

std::cout<<(1 << negzero)<<std::endl;
std::cout<<(1 >> negzero)<<std::endl;

Эта строка четко определена как зависящая от реализации:

std::cout<<(~negzero)<<(~zero)<<std::endl;

потому что реализация может иметь биты заполнения. Если нет никаких битов заполнения, на одной архитектуре дополнения ~zero является negzero, так ~negzero должен произвести 0 но я не мог найти в стандарте, если отрицательный ноль должен отображаться как 0 или как -0, Отрицательный плавающая запятая 0 должен отображаться со знаком минус, но для целое число отрицательное значение

Для последней 3 строки, включающей реляционные операторы и операторы равенства, в стандарте нет ничего явного, поэтому я бы сказал, что это определяется реализацией

TL / DR:

Внедрение в зависимости от:

std::cout<<(negzero < zero)<<std::endl;
std::cout<<(negzero <= zero)<<std::endl;
std::cout<<(negzero == zero)<<std::endl;
std::cout<<(~negzero)<<(~zero)<<std::endl;

Отлично определен и должен давать 1:

std::cout<<(1 << negzero)<<std::endl;
std::cout<<(1 >> negzero)<<std::endl;
3

Во-первых, архитектура дополнения (или даже различение отрицательного нуля) довольно редка, и для этого есть причина. В основном проще (аппаратно) добавить два дополнения, чем одно дополнение.

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

Отрицательные нули не должны быть такими простыми для создания (и если вам удастся это сделать, это в лучшем случае поведение, определяемое реализацией). Если это архитектура с одним дополнением, они будут созданы ~0 (побитовая инверсия), а не -0,

Стандарт C ++ довольно расплывчат в фактическом представлении и требованиях к поведению базовых типов (что означает, что спецификация имеет дело только с фактическим значением числа). Это означает, что вам в основном не повезло в связи внутреннего представления числа и его фактического значения. Так что даже если вы сделали это правильно и использовали ~0 (или каким-либо другим способом, подходящим для реализации) стандарт все еще не беспокоится о представлении, поскольку значение отрицательного нуля по-прежнему равно нулю.

#define zero (0)
#define negzero (~0)
std::cout<<(negzero < zero)<<std::endl;
std::cout<<(negzero <= zero)<<std::endl;
std::cout<<(negzero == zero)<<std::endl;
std::cout<<(~negzero)<<(~zero)<<std::endl;
std::cout<<(1 << negzero)<<std::endl;
std::cout<<(1 >> negzero)<<std::endl;

три первые строки должны выдавать такой же результат, как если бы negzero был определен так же, как zero, Третья строка должна выводить два нуля (поскольку стандарт требует, чтобы 0 быть представленным как 0 без знака-знака). Два последних должны выводить.

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

То, как связаны C и C ++, разумно думать, что отрицательный ноль будет получен таким же образом в C ++, как и в C, и стандарт, похоже, допускает это. В то время как стандарт C ++ допускает другие способы (через неопределенное поведение), но никакие другие, кажется, не доступны через определенное поведение. Таким образом, вполне вероятно, что если реализация C ++ сможет генерировать отрицательные нули разумным способом, это будет то же самое, что и для аналогичной реализации C.

-2
По вопросам рекламы [email protected]