Рассмотрим следующий код на архитектуре дополнения:
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;
Основываясь на моей интерпретации стандарта:
Стандарт 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:
Если реализация не поддерживает отрицательные нули, поведение
&, |, ^, ~, <<и >> операторы с операндами, которые будут производить
такое значение не определено.
Следовательно, приведет ли связанная операция к неопределенному поведению, зависит от того, поддерживает ли реализация отрицательные нули.
Прежде всего, ваша первая предпосылка неверна:
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;
Во-первых, архитектура дополнения (или даже различение отрицательного нуля) довольно редка, и для этого есть причина. В основном проще (аппаратно) добавить два дополнения, чем одно дополнение.
Код, который вы разместили, похоже, не имеет неопределенного поведения или даже поведения, определенного реализацией, вероятно, он не должен приводить к отрицательному нулю (или его не следует отличать от обычного нуля).
Отрицательные нули не должны быть такими простыми для создания (и если вам удастся это сделать, это в лучшем случае поведение, определяемое реализацией). Если это архитектура с одним дополнением, они будут созданы ~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.