Подтвердить что ты не робот

Правое смещение отрицательных чисел в C

У меня есть код C, в котором я делаю следующее.

int nPosVal = +0xFFFF;   // + Added for ease of understanding
int nNegVal = -0xFFFF;   // - Added for valid reason

Теперь, когда я пытаюсь

printf ("%d %d", nPosVal >> 1, nNegVal >> 1);

Я получаю

32767 -32768

Ожидается ли это?

Я могу думать что-то вроде

65535 >> 1 = (int) 32767.5 = 32767
-65535 >> 1 = (int) -32767.5 = -32768

То есть, -32767.5 округляется до -32768.

Правильно ли это понимание?

4b9b3361

Ответ 1

Похоже, ваша реализация, вероятно, выполняет арифметический сдвиг бит с двумя номерами дополнений. В этой системе он сдвигает все биты вправо, а затем заполняет верхние биты копией любого последнего бита. Итак, для вашего примера, рассматривая int как 32-бит здесь:

nPosVal = 00000000000000001111111111111111
nNegVal = 11111111111111110000000000000001

После смены у вас есть:

nPosVal = 00000000000000000111111111111111
nNegVal = 11111111111111111000000000000000

Если вы преобразуете это обратно в десятичное, вы получите 32767 и -32768 соответственно.

Эффективно правый сдвиг округляется к отрицательной бесконечности.

Изменить: В соответствии с разделом 6.5.7 последнего черновик проекта, это поведение при отрицательных числах зависимый от реализации:

Результат E1 → E2 - это E1 с правым сдвигом позиций E2. Если E1 имеет неподписанный тип или если E1 имеет подписанный тип и неотрицательное значение, значение результата является неотъемлемой частью частного E1/2 E2. Если E1 имеет подписанный тип и отрицательное значение, результирующее значение определяется реализацией.

Они заявили rational для этого:

Комитет C89 подтвердил свободу в реализации, предоставленную K & R, не требуя подписанная операция сдвига вправо, чтобы подписать расширение, поскольку такое требование может замедлить быстрый код, и поскольку полезность расширенных сдвигов знака является предельной. (Смещение отрицательного двойного дополнения целое арифметически право одно место not то же самое, что и деление на два!)

Таким образом, реализация зависит от теории. На практике я никогда не видел реализацию not арифметического сдвига вправо, когда левый операнд подписан.

Ответ 2

Нет, вы не получаете дробных чисел, например, 0,5 при работе с целыми числами. Результаты можно легко объяснить, когда вы смотрите на двоичные представления двух чисел:

      65535: 00000000000000001111111111111111
     -65535: 11111111111111110000000000000001

Бит сдвигается вправо на один бит и продолжается слева (обратите внимание, что это зависит от реализации, спасибо Trent):

 65535 >> 1: 00000000000000000111111111111111
-65535 >> 1: 11111111111111111000000000000000

Преобразование обратно в десятичное:

 65535 >> 1 = 32767
-65535 >> 1 = -32768

Ответ 3

В спецификации C не указывается, сдвинут ли знак бит или нет. Это зависит от реализации.

Ответ 4

Когда вы смещаете правую сторону, младший бит отбрасывается.

0xFFFF = 0 1111 1111 1111 1111, который сдвигает вправо, чтобы дать 0 0111 1111 1111 1111 = 0x7FFF

-0xFFFF = 1 0000 0000 0000 0001 (дополнение 2s), которое сдвигается вправо до 1 1000 0000 0000 0000 = -0x8000

Ответ 5

A-1: ​​Да. 0xffff → 1 - 0x7fff или 32767. Я не уверен, что делает -0xffff. Это странно.

A-2: Сдвиг - это не то же самое, что деление. Это бит-сдвиг - примитивная двоичная операция. То, что оно иногда может использоваться для некоторых типов деления, удобно, но не всегда одинаково.

Ответ 6

Под уровнем C машины имеют ядро ​​центрального процессора, которое полностью целочисленное или скалярное. Хотя в наши дни каждый настольный процессор имеет FPU, это не всегда так, и даже сегодня встроенные системы выполняются без инструкций с плавающей запятой.

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

Таким образом, инструкции CPU выполняют операции с фиксированной точкой, обычно обрабатываемые как чисто целочисленные операционные системы. Только если программа объявляет элементы float или double, будут существовать какие-либо фракции. (Ну, вы можете использовать CPU ops для "фиксированной точки" с фракциями, но это сейчас и всегда было довольно редко.)

Независимо от того, что требовалось комитетом по языковому стандарту несколько лет назад, все разумные машины распространяют знаковый бит на сдвигах справа от подписанных чисел. Правые сдвиги беззнаковых значений сдвигаются в нулях слева. Биты, сдвинутые справа, отбрасываются на пол.

Для дальнейшего понимания вам нужно исследовать "арифметику twos-complement".