Является ли смещение вправо неопределенным поведением для отрицательного числа в cpp и в java?

Чтобы оптимизировать мой код cpp, в некоторых случаях я пытаюсь использовать смещение вправо. Вот пример:

int main()
{
    int i = (1 - 2) >> 31;  // sizeof(int) == 4
    ...
    ...
}

Я напечатал i и получил -1. Это означает, что он будет использовать 1 вместо 0 для заполнения пустой позиции, если число отрицательное. Другими словами, -1 >> 31 работает следующим образом:

1111...1    <--- the result of (1 - 2), which is -1
1111...1    <--- -1 >> 31, 1 is used to fill in the empty position

Я просто хочу знать, четко ли определено это поведение или нет?

Если это UB в cpp, то как насчет Java?


person Yves    schedule 01.08.2017    source источник
comment
В Java определенно не undefined. >> делает расширение знака, >>> нет. См. Учебные руководства по Java™ — побитовые операторы и операторы побитового сдвига, или окончательный источник, спецификация языка Java, 15.19. Операторы сдвига.   -  person Andreas    schedule 01.08.2017
comment
как насчет Java? - Зачем останавливаться на Java, спросите об этом также в C# и Rust.   -  person StoryTeller - Unslander Monica    schedule 01.08.2017


Ответы (3)


Да. Это определяется реализацией.

Согласно C++03 5.8/3, который определяет сдвиг вправо:

Значение E1 >> E2 — это E1, сдвинутая вправо E2 битовых позиций. Если E1 имеет беззнаковый тип или если E1 имеет знаковый тип и неотрицательное значение, значение результата равно целой части частного E1, деленной на число 2, возведенное в степень E2. Если E1 имеет тип со знаком и отрицательное значение, результирующее значение определяется реализацией.

Для получения дополнительной информации см. этот ссылка.

person msc    schedule 01.08.2017
comment
stackoverflow.com/questions/11227809/ Не могли бы вы прочитать пятый комментарий принятого ответа на этот вопрос? Я не совсем понимаю, что он имел в виду... IB или UB... - person Yves; 01.08.2017
comment
@Yves Если вы имеете в виду комментарий, в котором говорится, что сдвиг вправо отрицательного целого числа со знаком - это IB, к нему мало что можно добавить, чтобы сделать его более понятным. - person molbdnilo; 01.08.2017
comment
@molbdnilo На данный момент я понимаю так: не сдвигайте бит, чтобы перезаписать бит знака, потому что это UB, а это означает, что сдвиг влево большого целого числа со знаком опасен. Принимая во внимание, что смещение вправо - это IB... - person Yves; 01.08.2017
comment
@Yves - это опасно не потому, что это UB, а потому, что результат может различаться в разных системах. - person Bo Persson; 01.08.2017

По умолчанию он подписан int. Диапазон от -32767 до 32767, побитовый диапазон от -111111111111111 до +111111111111111. Самый первый бит слева действует как отрицательный или положительный индикатор. И все арифметические операции будут выполняться методом дополнения до 2. В общем, отрицательный int представлен в методе двух дополнений, т.е. возьмем пример того, как представлен -1.

4 Bytes = 32 bits
0000 0000 0000 0000 0000 0000 0000 0000
how represent 1
0000 0000 0000 0000 0000 0000 0000 0001
Then we invert the digits. 0 becomes 1, 1 becomes 0.
1111 1111 1111 1111 1111 1111 1111 1110
Then we add 1.
1111 1111 1111 1111 1111 1111 1111 1111
This is how -1 is represented

Сдвиг вправо отрицательного числа определяется как сдвиг на 1 с к старшим битовым позициям, тогда в дополнительном представлении на 2 с он будет вести себя как арифметический сдвиг - результат сдвига вправо на N будет таким же, как деление на 2N , округляя в сторону минус бесконечности. Таким образом, смещение -1 равно -1, теперь возьмите другое число. Например, если у вас есть 8-битное двоичное число с дополнением до 2, пусть оно представляет -3

    0000 0011
    Then we invert the digits.
    1111 1100
    Then we add 1. 
    1111 1101

11111101, представляющее -3 в десятичном виде, и вы выполняете арифметический сдвиг вправо на 1, чтобы получить 11111110, представляющий -2 в десятичном виде, это то же самое, что и деление -3 на 2 ^ 1, что дает -1,5, которое округляется до отрицательной бесконечности, что приводит к -2 .

person SAI ESWAR    schedule 01.08.2017
comment
Хороший ответ для новичка ... и просто для протокола: не забывайте о принятии ответов ... работает даже для вопросов, которые были закрыты для вас. И счастливого голосования, теперь, когда вы достигли этого уровня ;-) - person GhostCat; 04.09.2017

В Java поведение >> четко определено для отрицательных чисел (см. ниже).

В C++ поведение >> не определено для отрицательных чисел (см. ответ от rsp ).


Цитирование спецификации языка Java, §15.19. Операторы смены:

Значение n ›› с равно n сдвигу вправо с битовых позиций с расширением знака . Полученное значение равно floor(n / 2s). Для неотрицательных значений n это эквивалентно усечению целочисленного деления, вычисляемого оператором целочисленного деления /, на два в степени s.

Значение n ››› с равно n сдвигу вправо с битовых позиций с нулевым расширение, где:

  • Если n положительное, то результат будет таким же, как и у n ›› s.

  • Если n отрицательное и тип левого операнда int, то результат равен результату выражения (n ›› s) + (2 ‹‹ ~s) .

  • Если n отрицательное и тип левого операнда long, то результат равен результату выражения (n ›› s) + (2L ‹‹ ~s) .

person Andreas    schedule 01.08.2017
comment
Для C++ поведение определено определено, но это определение может варьироваться в разных системах. Предположительно, он делает то, что делает аппаратное обеспечение. - person Bo Persson; 01.08.2017
comment
@BoPersson Это означает, что если вы не знаете, где будет работать ваш код, он не определен, но я понимаю ваше различие. - person Andreas; 01.08.2017
comment
@Andreas: Undefined Behavior интерпретируется современными компиляторами как приглашение вести себя бессмысленно во имя оптимизации, что полностью отличается от получения значения, определенного реализацией или неопределенного. Например, учитывая unsigned mulMod65536(unsigned short x, unsigned short y) { return (x*y) & 0xFFFF;}, можно не видеть никаких причин, по которым должно произойти что-то странное, если x*y превышает 0x7FFFFFFF, но в gcc такая функция иногда будет иметь странные побочные эффекты, если x*y не вписывается в int. Реализация-определенная не допускает такой лицензии. - person supercat; 01.08.2017
comment
@supercat Undefined просто означает, что спецификация/API не определяет/не определяет поведение. Это не означает (обязательно) случайное/произвольное, просто реализация может делать то, что ей нравится, и может свободно изменять то, что ей нравится, в любое время. Это означает, что мы, как пользователи этой функции, не можем полагаться на какой-либо конкретный результат. Вопрос не имеет контекста реализации, и, не зная точной реализации, реализация-определенная фактически означает то же самое, что и неопределенное, т. е. вы понятия не имеете, что произойдет, поэтому >> является неопределенным для целей этого вопроса. - person Andreas; 01.08.2017
comment
@Andreas: Хотя некоторые здравомыслящие компиляторы признают, что имеет смысл вести себя ... документально, характерным для среды [один из предлагаемых типичных результатов UB, перечисленных в Стандарте], когда базовая платформа имеет естественное поведение, современный компиляторы не могут полагаться на это. Оценка (i*65535) & 65535u внутри for(int i=32767; i<z; i++) заставит gcc предположить, что цикл будет выполняться ноль, один или два раза на основе z, даже если нет других доказательств того, что z не может быть больше 32769. - person supercat; 01.08.2017