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

Почему такой сложный код испущен для деления целого числа со знаком на два?

Когда я компилирую этот код с VС++ 10:

DWORD ran = rand();
return ran / 4096;

Я получаю эту разборку:

299: {
300:    DWORD ran = rand();
  00403940  call        dword ptr [__imp__rand (4050C0h)]  
301:    return ran / 4096;
  00403946  shr         eax,0Ch  
302: }
  00403949  ret

который является чистым и кратким и заменяет деление на две силы с логическим сдвигом вправо.

Но когда я компилирую этот код:

int ran = rand();
return ran / 4096;

Я получаю эту разборку:

299: {
300:    int ran = rand();
  00403940  call        dword ptr [__imp__rand (4050C0h)]  
301:    return ran / 4096;
  00403946  cdq  
  00403947  and         edx,0FFFh  
  0040394D  add         eax,edx  
  0040394F  sar         eax,0Ch  
302: }
  00403952  ret

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

Какая потребность в этих дополнительных манипуляциях? Почему недостаточно арифметического сдвига?

4b9b3361

Ответ 1

Причина в том, что беззнаковое деление на 2 ^ n может быть реализовано очень просто, тогда как подписанное деление несколько сложнее.

unsigned int u;
int v;

u / 4096 эквивалентен u >> 12 для всех возможных значений u.

v / 4096 НЕ эквивалентен v >> 12 - он ломается, когда v < 0, так как направление округления отличается для переключения по отношению к делению, когда задействованы отрицательные числа.

Ответ 2

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

Например, -1 >> 1 - -1, тогда как -1/2 - 0.

Ответ 3

Из стандарта C:

Когда целые числа делятся, результатом оператора/является алгебраическое отношение с любой дробной частью, отброшенной .105) Если фактор a/b является представимым, выражение (a/b) * b + a% b должно равен a; в противном случае поведение как a/b, так и a% b undefined.

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

(-8191) / 4096 -> -1
(-8191) % 4096 -> -4095

которая удовлетворяет уравнению, тогда как

(-8191) >> 12 -> -2 (assuming arithmetic shifting)

не является делением с усечением, поэтому -2 * 4096 - 4095, безусловно, не равно -8191.

Обратите внимание, что сдвиг отрицательных чисел на самом деле определяется реализацией, поэтому выражение C (-8191) >> 12 не имеет в целом правильного результата в соответствии со стандартом.